380 likes | 496 Views
Type-level functions for Haskell. Tom Schrijvers K.U.Leuven , Belgium. with Manuel Chakravarty , Martin Sulzmann and Simon Peyton Jones. Type-level functions. Functional programming at the type level!. Examples Usefulness Interaction Type Checking
E N D
Type-level functions for Haskell Tom Schrijvers K.U.Leuven, Belgium with Manuel Chakravarty, Martin Sulzmann and Simon Peyton Jones
Type-level functions Functional programming at the type level! • Examples • Usefulness • Interaction • Type Checking • Eliminating functional dependencies
Example: 1 + 1 = 2? -- Peano numerals dataZ dataSuccn -- Abbreviations typeOne = Succ Z typeTwo = Succ One -- Type-level addition typefamilySummn typeinstanceSumZn = n typeinstanceSum (Succa) b = Succ (Suma b) type function declaration Sum :: * -> * -> * 1st type function instance 2nd type function instance
Example: 1 + 1 = 2? -- Type-level addition typefamilySummn typeinstanceSumZnat = nat typeinstanceSum (Succa) b = Succ (Suma b) -- Hypothesis hypthesis :: (Sum One One, Two) hypothesis = undefined -- Test test :: (a,a) -> Bool test _ = True
Example: 1 + 1 = 2? -- Hypothesis hypthesis :: (Sum One One, Two) hypothesis = undefined -- Test test :: (a,a) -> Bool test _ = True -- Proof proof :: Bool proof = test hypothesis static check: 1+1 = 2
Example: Length-indexed Lists -- Length-indexed lists : vectors dataVec e lwhere VNil :: Vec e Z VCons :: e -> Vec e l -> Vec e (Succ l) -- Safe zip: matched lengths vzip :: Vec a l -> Vec b l -> Vec (a,b) l vzipVNilVNil = VNil vzip (VCons x xs) (VCons y ys) = VCons (x,y) (vzipxsys)
Example: Length-indexed Lists -- Safe zip: matched lengths vzip :: Vec a l -> Vec b l -> Vec (a,b) l vzipVNilVNil = VNil vzip (VCons x xs) (VCons y ys) = VCons (x,y) (vzipxsys) 1≠2 > let l1 = (VCons 1 VNil) l2 = (VCons ‘a’ (VCons ‘b’ VNil)) in vzip l1 l2
Example: Length-indexed Lists -- Safe zip: matched lengths vzip :: Vec a l -> Vec b l -> Vec (a,b) l vzipVNilVNil = VNil vzip (VCons x xs) (VCons y ys) = VCons (x,y) (vzipxsys) 2=2 > let l1’ = (VCons 1 (VCons 2 VNil)) l2 = (VCons ‘a’ (VCons ‘b’ VNil)) in vzip l1’ l2 VCons (1,’a’) (VCons (2,’b’) VNil)
Example: Length-indexed lists -- concatenation vconc :: Vec a m -> Vec a n -> Vec a ??? vconcVNilVNil= VNil Vconc (VCons x xs) ys = VCons x (vconcxsys)
Example: Length-indexed lists -- concatenation vconc :: Vec a m -> Vec a n -> Vec a (Sum m n) vconcVNilVNil= VNil Vconc (VCons x xs) ys = VCons x (vconcxsys) 1+1=2 > let l1 = (VCons 1) l2 = (VCons ‘a’ (VCons ‘b’ VNil)) l3 = vconc l1 l1 in vzip l3 l2 VCons (1,’a’) (VCons (1,’b’) VNil)
Example: Collections (1/4) classCollection c where typeElem c add :: Elem c -> c -> c list :: c -> [Elem c] elem :: c -> Elem c Associated value function elem :: c -> Elem c Associated type function Elem :: * -> *
Example: Collections (2/4) instanceCollection [e] where typeElem [e] = e type functions are open! instanceCollection (Tree e) where typeElem (Tree e) = e instanceCollection BitVectorwhere typeElemBitVector = Bit
Example: Collections (3/4) instanceCollection [e] where typeElem [e] = e add :: e -> [e] -> [e] add x xs = x : xs list :: [e] -> [ e ] list xs = xs instanceCollection [e] where typeElem [e] = e add :: Elem [e] -> [e] -> [e] add x xs = x : xs list :: [e] -> [Elem [e]] list xs = xs
Example: Collections (4/4) context constraint, satisfied by caller addAll :: c1 -> c2 -> c2 addAllxsys = foldr add ys (list xs) addAll :: Collection c1 => c1 -> c2 -> c2 addAllxsys = foldr add ys (list xs) addAll :: Collection c1, Collection c2 => c1 -> c2 -> c2 addAllxsys = foldr add ys (list xs) addAll :: Collection c1, Collection c2, Elem c1 ~ Elem c2=> c1 -> c2 -> c2 addAllxsys = foldr add ys (list xs) Elem [Bit] ~ Elem BitVector > addAll [0,1,0,1,0] emptyBitVector <010101>
Summary of Examples • functions over types • open definitions • stand-alone • associated to a type class • equational constraints in signature contexts • usefulness • limited on their own • really great with • GADTs • type classes
Compiler Overview label elem [0] == 0 γ : e.Elem [e] = e A axiom Elem [Int] ~ Int ? hypothesis γInt :: Elem [Int] ~ Int ! proof cast witness (elem [0]) ► (γInt) == 0
Type Checking = Syntactical? • Basic Hindley-Milner [Int] ~ [Int] syntactic equality (unification) • Type Functions Elem [Int] ~ Int syntactic equality modulo equational theory
Type Checking = Term Rewriting • Equational theory = given equations • Toplevel equations: Et • Context equations: E g • Theorem proving • Operational interpretation of theory • = Term Rewriting System (Soundness!) γ : e.Elem [e] = e A Elem [Int] ~ Int γInt Int Int ≡ Int
Completeness TRS must be Strongly Normalizing (SN): • Confluent: every type has a canonical form • Terminating • Practical & Modular Conditions: • Confluence: • no overlap of rule heads • Terminating: • Decreasing recursive calls • No nested calls
Completeness Conditions • Practical & Modular Conditions: • Confluence: no overlap • Elem [e] = e • Elem [Int] = Bit • Terminating: • Decreasingrecursivecalls • Elem [e] = Elem [[e]] • Nonested calls • Elem [e] = Elem (F e) • F e = [e]
Term Rewriting Conditions • We can do better: • Et: satisfy conditions • Eg: free form (but no schema variabels) √ Et Et √ Eg Eg’ Completion!
Type Checking Summary Et It’s all been implemented in GHC! Eg Eg’ 3.Syntactic Equality 1.Completion t ~ t t1 ~ t2 2.Rewriting
Functional Dependencies • Using relations as functions • Logic Programming! classCollection c e | c -> e where add :: e -> c -> c list :: c -> [e] elem :: c -> [e] instanceCollection [e] e where ...
FDs: Solution 2 • Backward Compatibility??? • Expressiveness lost??? FDs TFs automatic transformation
FDs: Simple Transformation Minimal Impact: class and instance declarations only classFD c ~ e => Collection c ewhere type FDc add :: e -> c -> c list :: c -> [e] elem :: c -> e instanceCollection [e] e where ... classFD c ~ e => Collection c ewhere type FDc add :: e -> c -> c list :: c -> [e] elem :: c -> e instanceCollection [e] e where type FD[e] = e classCollection c e | c -> e where add :: e -> c -> c list :: c -> [e] elem :: c -> [e] instanceCollection [e] e where ... classCollection c e | c -> e where type FDc add :: e -> c -> c list :: c -> [e] elem :: c -> e instanceCollection [e] e where ...
FDs: Advanced Transformation Drop dependent parameters classFD c ~ e => Collection c where type FDc add :: e -> c -> c list :: c -> [e] elem :: c -> e instanceCollection [e] e where type FD[e] = e classCollectionc where type FDc add :: FD c-> c -> c list :: c -> [FD c] elem :: c -> FD c instanceCollection [e] where type FD[e] = e classFD c ~ e => Collection c ewhere type FDc add :: e -> c -> c list :: c -> [e] elem :: c -> e instanceCollection [e] e where type FD[e] = e
FDs: Advanced Transformation Bigger Impact: signature contexts too classFD c ~ e => Collectionc ewhere type FDc addAll :: Collection c1 e, Collection c2 e => ... classCollectionc where type FDc addAll :: Collection c1 e, Collection c2 e => ... classCollectionc where type FDc addAll :: Collection c1, Collection c2, Elem c1 ~ Elem c2 => ...
Conclusion • Type checking with type functions = Term Rewriting • sound, complete and terminating • completion algorithm • Seemless integration in Haskell’s rich type system • Type classes • GADTs • Understanding functional dependencies better
Future Work • Improvements and variations • Weaker termination conditions • Closed type functions • Rational tree types • Efficiency • Applications: • Port libraries • Revisit type hacks • Write some papers!
Rational tree types -- Non-recursive list data List’ e t = Nil | Cons e t -- Fixedpoint operator typefamilyFix (k :: *->*) typeinstanceFix k = k (Fix k) -- Tie the knot type List e = Fix (List’ e) = List’ e (List’ e (List’ e …))