true, true - GPD - Universidad Complutense de Madrid

advertisement
Implementing Type Classes using
Type-Indexed Functions
Enrique Martín Martín
emartinm@fdi.ucm.es
Dpto. Sistemas Informáticos y Computación
Universidad Complutense de Madrid
II Taller de Programación Funcional
Valencia - 7 de Septiembre de 2010
Type classes and FLP



Type classes provide a clean and modular way
of writing overloaded functions.
Type classes are usually implemented using
dictionaries.
However, dictionaries have some problems in
FLP:


Bad interaction with non-determinism and call-time
choice.
Relatively complex translated programs: length,
readability...
Example using type classes
class arb A where
arb :: A
class arb A => foo A where
foo :: bool -> A
instance arb bool where
arb = true
arb = false
instance foo bool where
foo true = true
foo false = arb
f :: foo A => (A, A)
f = (arb, foo true)
Translation with dictionaries (I):
classes
data dictArb A = dArb A
class arb A where
arb :: A
arb :: dictArb A -> A
arb (dArb Farb) = Farb
data dictFoo A =
dFoo (dictArb A) (bool -> A)
class arb A => foo A where
foo :: bool -> A
foo :: dictFoo A -> A
foo (dFoo Darb Ffoo) = Ffoo
getArbFromFoo :: dictFoo A -> dictArb A
getArbFromFoo (dFoo Darb Ffoo) = Darb
Translation with dictionaries (II):
instances
instance arb bool where
arb = true
arb = false
instance foo bool where
foo true = true
foo false = arb
arbBool :: bool
arbBool = true
arbBool = false
dictArbBool :: dictArb bool
dictArbBool = dArb arbBool
fooBool :: bool -> bool
fooBool true = true
arbBool
fooBool false =
arb dictArbBool
dictFooBool :: dictFoo bool
dictFooBool =
dFoo dictArbBool fooBool
Translation with dictionaries (III):
functions
f :: foo A => (A, A)
f = (arb, foo true)
f :: dictFoo A -> (A, A)
f Dfoo = (arb (getArbFromFoo Dfoo), foo Dfoo true)
Drawbacks of dictionaries (I)

Missing answers in FLP with non-determinism:
[Type-classes and call-time vs. run-time, Wolfgang Lux, Curry mailing list]
arbL2 :: arb A => [A]
arbL2 = [arb, arb]
arbL2 :: dictArb A -> [A]
arbL2 Darb = [arb Darb, arb Darb]
Drawbacks of dictionaries (I)
> arbL2::[bool]
arbL2 dictArbBool → [arb dictArbBool, arb dictArbBool]
→* [arb (dArb true), arb (dArb true)]
→* [true, true]
> arbL2::[bool]
arbL2 dictArbBool → [arb dictArbBool, arb dictArbBool]
→* [arb (dArb false), arb (dArb false)]
→* [false, false]

Missing answers: [true, false], [false, true]
Drawbacks of dictionaries (II)

Long and complex resulting program.

Efficiency sink: use of projecting functions

arb (dArb Farb) = Farb

foo (dFoo Darb Ffoo) = Ffoo

getArbFromFoo (dFoo Darb Ffoo) = Darb
However efficiency can be optimized
[Implementing Haskell overloading, Lennart Augustsson].
Main idea: new translation



Translation of type classes in FLP using typeindexed functions (TIF): functions with a
different behaviour for each different type.
The translation is well-typed in a new liberal
type system for FLP.
Intuition:


Each overloaded function (function in a type
class) define a TIF.
Each instance adds new rules to TIFs.
Outline

Type system.

Translation using TIFs and type witnesses.

Example.

Advantages of the translation.

Conclusions.

Future work.
Type system

New type system for FLP [APLAS'10].




Type declarations are mandatory.
Type derivation/inference for expressions is similar
to Hindley-Milner.
Well-typedness of a program proceeds rule by
rule.
Posibilities for generic programming: generic
functions, type-indexed functions.
Type system


A program rule is well-typed if the righthand side fixes the types of the arguments
and the result less than the left-hand side.
It guarantees type preservation.
Type system: example
size
size
size
size
size
eq
eq
eq
eq
eq
:: A -> nat
false = s z
true = s z
z = s z
(s X) = s (size X)
:: A -> A -> bool
true true = true
false false = true
z z = true
(s X) (s Y) = eq X Y
Type system: example
size :: A -> nat
size false = s z
size true = s z
size z = s z
z :: nat X)
size ::(s
size false
natX) = s s(size
eq
eq
eq
eq
eq
:: A -> A -> bool
true true = true
false false = true
z z = true
(s X) (s Y) = eq X Y
Type system: example
size :: A -> nat
size false = s z
size true = s z
size z = s z
size (s X) = s (size X)
s z :: nat
size true :: nat
eq :: A -> A -> bool
eq true true = true
eq false false = true
eq z z = true
eq (s X) (s Y) = eq X Y
Type system: example
size
size
size
size
size
:: A -> nat
false = s z
true = s z
z = s z
(s X) = s (size X)
eq :: A -> A -> bool
eq true true =X ::
true
X :: nat
A
size (s
:: nat falses (size
eqX)false
= true
X) :: nat
eq z z = true
eq (s X) (s Y) = eq X Y
Type system: example
size
size
size
size
size
eq
eq
eq
eq
eq
:: A -> nat
false = s z
true = s z
z = s z
(s X) = s (size X)
:: A -> A -> bool
true true = true
false false = true
z z = true
(s X) (s Y) = eq X Y
Type system: example
size
size
size
size
size
:: A -> nat
false = s z
true = s z
z = s z
(s X) = s (size X)
X :: nat
X :: A
bool
Y :: nat eq :: A -> A ->Y ::
A
true
eq (s X) (seq
Y) true
:: booltrue = eq
X Y :: bool
eq false false = true
eq z z = true
eq (s X) (s Y) = eq X Y
Type system: example
size
size
size
size
size
eq
eq
eq
eq
eq
:: A -> nat
false = s z
true = s z
z = s z
(s X) = s (size X)
:: A -> A -> bool
true true = true
false false = true
z z = true
(s X) (s Y) = eq X Y
Type system: ill-typed example
snd :: A -> B -> B
unpack :: (A -> A) -> B
unpack (snd X) = X
f :: bool -> A
f true = z
f false = true
Type system: ill-typed example
snd :: A -> B -> B
unpack :: (A -> A) -> B
unpack (snd X) = X
f :: bool -> A
X :: A
f true = z
unpack
(snd X)= ::true
B
f false
X :: C
Type system: ill-typed example
snd :: A -> B -> B
unpack :: (A -> A) -> B
unpack (snd X) = X
f :: bool -> A
f true = z
f false = true
f true :: A
z :: nat
Type system: ill-typed example
snd :: A -> B -> B
unpack :: (A -> A) -> B
unpack (snd X) = X
f :: bool -> A
f true = z
f false = true
f false :: A
true :: bool
Type system: ill-typed example
snd :: A -> B -> B
unpack :: (A -> A) -> B
unpack (snd X) = X
f :: bool -> A
f true = z
f false = true
Translation of type classes using
TIFs



Replace each overloaded function by a TIF.
Each rule of an overloaded function in an
instance is a rule of the corresponding TIF.
The TIF is implemented using type witnesses
to determine which rules to apply.
Translation (I): Type witnesses

A way of representing types as values.

Extends the data type with a new constructor.

Examples:

data nat = z | s nat | #nat

data bool = true | false | #bool

data [A] =
nil | cons A (list A) | #list A

[bool] → #list #bool :: [bool]

[[nat]] → #list (#list #nat) :: [[nat]]
Translation (II): classes and
instances
class arb A where
arb :: A
class arb A => foo A where
foo :: bool -> A
arb :: A -> A
foo :: A -> bool -> A
instance arb bool where
arb = true
arb = false
arb #bool = true
arb #bool = false
instance foo bool where
foo true = true
foo false = arb
foo #bool true = true
foo #bool false = arb #bool
Could be optimized
Translation (III): functions
f :: foo A => (A, A)
f = (arb, foo true)
f :: A -> (A, A)
f WA = (arb WA, foo WA true)
Advantages (I): no missing
answers

No missing answers
arbL2 :: arb A => [A]
arbL2 = [arb, arb]
arbL2 :: A -> [A]
arbL2 WA = [arb WA, arb WA]
Advantages (I): no missing
answers
> arbL2::[bool]
arbL2 #bool → [arb #bool, arb #bool]
→ [true, arb #bool]
→ [true, true]
> arbL2::[bool]
arbL2 #bool → [arb #bool, arb #bool]
→ [false, arb #bool]
→ [false, true]
> arbL2::[bool]
arbL2 #bool → [arb #bool, arb #bool]
→ [true, arb #bool]
→ [true, false]
Advantages (II): efficiency
Advantages (II): efficiency


Test program: traversing a list applying
overloaded functions inside a big class
hierarchy.
Explanation of the results:

Big class hierarchy → big dictionaries.
(a) Big dictionaries must be passed.
(b) Functions must be projected from big dictionaries.
Advantages (III): simplicity
data dictArb A = dArb A
data bool = true |
false | #bool
arb :: dictArb A -> A
arb (dArb Farb) = Farb
data dictFoo A =
dFoo (dictArb A) (bool -> A)
arb :: A -> A
foo :: A -> bool -> A
foo :: dictFoo A -> A
foo (dFoo Darb Ffoo) = Ffoo
getArbFromFoo :: dictFoo A -> dictArb A
getArbFromFoo (dFoo Darb Ffoo) = Darb
arbBool :: bool
arbBool = true
arbBool = false
dictArbBool :: dictArb bool
dictArbBool = dArb arbBool
fooBool :: bool -> bool
fooBool true = true
fooBool false = arb dictArbBool
dictFooBool :: dictFoo bool
dictFooBool = dFoo dictFooBool fooBool
f :: dictFoo A -> (A, A)
f Dfoo = (arb (getArbFromFoo Dfoo),
foo Dfoo true)
vs.
arb #bool = true
arb #bool = false
foo #bool true = true
foo #bool false =
arb #bool
f :: A -> (A, A)
f WA = (arb WA,
foo WA true)
Conclusions



New and simple translation of type classes
for FLP relying on a new liberal type system.
Solve some problems of missing answers.
Perform faster than dictionaries without
optimizations (at least in some cases).
Future work

Formalize the translation.

Implement and integrate into TOY.

Study optimizations of the translation.

Study how the translation combines with
modules and separate compilation (open
functions).
Thanks!
Download