320 likes | 461 Views
Do-it-yourself dictionaries in Haskell. Ingmar Brouns & Lukas Spee. Introduction. Extending the Haskell class system Allowing multiple implementations for one instance of a class declaration. Introduction – examples (1). An example from the Java world:
E N D
Do-it-yourself dictionaries in Haskell Ingmar Brouns & Lukas Spee Do-it-yourself dictionaries in Haskell
Introduction • Extending the Haskell class system • Allowing multiple implementations for one instance of a class declaration Do-it-yourself dictionaries in Haskell
Introduction – examples (1) • An example from the Java world: Equality: based on reference equivalence or member field value equivalence? Do-it-yourself dictionaries in Haskell
Introduction – example (2) • In Haskell: Ordering on Strings with the (>) operator: based on alfabetical order (standard) or on length? • Filename organizer vs Pretty printer: lexicographical equality vs length equality Do-it-yourself dictionaries in Haskell
Issues • Not sufficient for examples • We need to extend the class system • How does the current system work ? • What different syntaxes will be an option (describing with intuitive semantics) • How does the new syntax reflect on the semantics ? Do-it-yourself dictionaries in Haskell
Current Class System • Define class & instance declarations • Translation, to a core-Haskell variant without overloading, by preprocessor • How does this translation work ? Do-it-yourself dictionaries in Haskell
Translation to core-Haskell • Class declarations are translated to dictionary data type declarations • Instance declarations are translated to instances of the corresponding dictionary data type Do-it-yourself dictionaries in Haskell
Translation - example class Eq a => Ord a where (>) :: a -> a -> Bool will be translated to: data OrdDict a = OrdDict { gt’ :: a -> a -> Bool, …} Do-it-yourself dictionaries in Haskell
Translation - example instance Ord Char where (>) = primitiveGT will be translated to: ordDictChar :: OrdDict Char ordDictChar = OrdDict { gt’ = primitiveGT, …} Do-it-yourself dictionaries in Haskell
Translation - example foo :: Ord a => a -> a -> Bool foo = \x y -> x > y will be translated to: foo :: OrdDict a -> a -> a -> Bool foo = \ordDict x y -> gt’ ordDict x y So, in fact, dictionaries are implicit parameters that ‘control’ the overloading Do-it-yourself dictionaries in Haskell
Translation - example Which instance of OrdDict a is used for the application of foo depends on the types of the parameters, thus: foo ‘a’ ‘b’ will be translated to: foo ordDictChar ‘a’ ‘b’ Do-it-yourself dictionaries in Haskell
Back to our example • We now have an instance of Ord for Char • We need an instance of Ord for String ([Char]) • Can we construct it from our Ord Char instance ? Do-it-yourself dictionaries in Haskell
Back to our example Yes, but we need an instance of Ord [a] for this: instance Ord a => Ord [a] where (>) [] _ = False (>) _ [] = True (>) (x:xs) (y:ys) = (x > y) && (xs > ys) Do-it-yourself dictionaries in Haskell
A dictionary for Ord [a] ordDictList :: OrdDict a -> OrdDict [a] ordDictList ordD = OrdDict (ordList ordD) ordList :: OrdDict a -> [a] -> [a] -> Bool ordList ordDict (x:xs) [] = True ordList ordDict [] (y:ys) = False ordList ordDict (x:xs) (y:ys) = gt’ ordDict x y && gt’ (ord ordDict) xs ys Please remember this for one more slide Do-it-yourself dictionaries in Haskell
A dictionary for Ord [Char] Now we can easily combine the two to create the dictionary for Ord String: ordDictString = ordDictList ordDictChar That’s nice, but how is it going to solve our problem? Do-it-yourself dictionaries in Haskell
Dictionaries are the answer! • If we want a new implementation for (>) on String, we’ll make it and put it in our own dictionary • Wherever we want to use this dictionary instead of the implicitly passed default dictionary, we make this explicit Do-it-yourself dictionaries in Haskell
Back to our example Suppose we have our own dictionary for Ord [Char] called ‘impl2’ which defines (>) as: (>) :: String -> String -> Bool (>) = \x y -> (length x) > (length y) We can now explicitly pass this dictionary to a function: getMaximumSize :: String -> String -> Int getMaximumSize {impl2} = \x y -> if x > y then (length x) else (length y) Do-it-yourself dictionaries in Haskell
Extending the syntax • Several attempts • What are the drawbacks ? • How can they be avoided ? Do-it-yourself dictionaries in Haskell
Attempt 1 Allow users to define dictionaries explicitly instance Ord [Char] where (>) [] _ = False (>) _ [] = True (>) (x:xs) (y:ys) = (x > y) && (xs > ys) ... ordDictString’ = OrdDict { gt’ = (\a b -> compare (length a) (length b) == GT), ...} Do-it-yourself dictionaries in Haskell
Attempt 1 • Drawbacks: • User has to be familiar dictionaries as used in core-Haskell • Dictionary is copied so user must use implementations of overloaded functions in definition • Implementation is no longer hidden Do-it-yourself dictionaries in Haskell
Attempt 2 • Define extra implementation within instance declaration • Tag the extra implementation instance Ord [Char] where (>) [] _ = False (>) _ [] = True (>) (x:xs) (y:ys) = (x > y) && (xs > ys) (>) a b = length a > length b {impl2} Do-it-yourself dictionaries in Haskell
Attempt 2 • Good things: • Implementation not visible to user • Consistent with existing syntax • Drawbacks • Unhandy when using modules Do-it-yourself dictionaries in Haskell
Attempt 3 Name the instance declaration impl2 = instance Ord [Char] where (>) a b = length a > length b Capable of modifying instances in other modules Do-it-yourself dictionaries in Haskell
Attempt 4 Which instance declaration do we want to override ? Impl2 overrides impl = instance Ord [Char] where (>) a b = length a > length b When no overrides statement -> default Only one default allowed, explicit or implicitly defined Do-it-yourself dictionaries in Haskell
Type Checking Where do we need to do new checks ? • Type checks: • Type of new implementation should correspond to type of overridden implementation • explicit dictionary usage should have same type as declared in explicit type Do-it-yourself dictionaries in Haskell
Formal typing / translation rules Do-it-yourself dictionaries in Haskell
Formal typing / translation rules • Now: • gt :: Ord a => a -> a -> Bool • Will be written as • gt :: forall a. (ord :: Ord a) . a -> a -> Bool • Eq a is an abbrevation for (a -> a -> Bool) • But now the important part !!! Do-it-yourself dictionaries in Haskell
Typing / translation rules • Translation is controlled by typing A,(ord :: Ord Char \ ordDictChar) |- e :: p \ ē A |- e :: (ord :: Ord Char).p \ (\ ordDictChar. ē) • In the current system, there can only be one instance with a certain type • In our system this is not the case Do-it-yourself dictionaries in Haskell
Typing / translation rules • Result: • Translation is no longer only type driven • Now also name driven • For example not passing a list of dictionaries, but a list of (name, dictionary) tuples. Do-it-yourself dictionaries in Haskell
Implementation issues for UHC • Preprocessor has to be adjusted • Overriding dictionary should have same type as overridden dictionary • Named dictionaries have to be generated • So the context for passing dictionaries will have to be adjusted • User defined dictionary usage has to be type checked Do-it-yourself dictionaries in Haskell
Conclusion • Allowing users to define alternative implementations of class instances, gives them more freedom. • Formal typing rules do not change, only the translation is now also dependent of names • Some extra type checks will be needed • It is possible to implement this in UHC Do-it-yourself dictionaries in Haskell
Questions • Now it is the case that the translation is purely type driven. What is the case with our extension, and explain why it is like that ? • How does this affect the existing typing rules ? • Explain the use of our overrides statement. Do-it-yourself dictionaries in Haskell