Microsoft Research University of Pennsylvania

Slides:



Advertisements
Similar presentations
Types and Programming Languages Lecture 13 Simon Gay Department of Computing Science University of Glasgow 2006/07.
Advertisements

Functional Programming Lecture 10 - type checking.
Tom Schrijvers K.U.Leuven, Belgium with Manuel Chakravarty, Martin Sulzmann and Simon Peyton Jones.
Reconciling OO and Haskell-Style Overloading Mark Shields Simon Peyton Jones Microsoft Research Cambridge
Let should not be generalized Dimitrios Vytiniotis, Simon Peyton Jones Microsoft Research, Cambridge TLDI’1 0, Madrid, January 2010 Tom Schrijvers K.U.
Extended Static Checking for Haskell (ESC/Haskell) Dana N. Xu University of Cambridge advised by Simon Peyton Jones Microsoft Research, Cambridge.
Kathleen Fisher cs242 Reading: “A history of Haskell: Being lazy with class”,A history of Haskell: Being lazy with class Section 6.4 and Section 7 “Monads.
Type inference and modern type systems Stephanie Weirich University of Pennsylvania.
CATCH: Case and Termination Checker for Haskell Neil Mitchell (Supervised by Colin Runciman)
Parallelism and Concurrency Koen Lindström Claessen Chalmers University Gothenburg, Sweden Ulf Norell.
A Lightning Tour of Haskell Lecture 1, Designing and Using Combinators John Hughes.
Good Advice for Type-directed Programming Aspect-oriented Programming and Extensible Generic Functions Geoffrey Washburn [ ] Joint.
Generative type abstraction and type-level computation (Wrestling with System FC) Stephanie Weirich, Steve Zdancewic University of Pennsylvania Dimitrios.
Scrap your boilerplate with class Ralf Lämmel, Simon Peyton Jones Microsoft Research.
Type Inference: CIS Seminar, 11/3/2009 Type inference: Inside the Type Checker. A presentation by: Daniel Tuck.
Generic Programming with Dependent Types Stephanie Weirich University of Pennsylvania.
Scrap Your Boilerplate /~simonpj/papers/hmap/
Advanced Functional Programming 2009 Ulf Norell (lecture by Jean-Philippe Bernardy)
Sound Haskell Dana N. Xu University of Cambridge Joint work with Simon Peyton Jones Microsoft Research Cambridge Koen Claessen Chalmers University of Technology.
1 Functional Programming Lecture 6 - Algebraic Data Types.
Hack for HHVM Converting Facebook Julien Verlaguet Software Engineer.
Lee CSCE 314 TAMU 1 CSCE 314 Programming Languages Interactive Programs: I/O and Monads Dr. Hyunyoung Lee.
First Order Haskell Neil Mitchell York University λ.
Scrap your boilerplate: generic programming in Haskell Ralf Lämmel, Vrije University Simon Peyton Jones, Microsoft Research.
Advanced Functional Programming Tim Sheard 1 Lecture 17 Advanced Functional Programming Tim Sheard Oregon Graduate Institute of Science & Technology Lecture:
1 Static Contract Checking for Haskell Dana N. Xu University of Cambridge Joint work with Simon Peyton Jones Microsoft Research Cambridge Koen Claessen.
Today’s lecture Review chapter 5 go over exercises.
Shifting the Blame A blame calculus with delimited control Taro Sekiyama* Atsushi Igarashi Soichiro Ueda Kyoto University Gradual typing.
INF3110 Group 2 EXAM 2013 SOLUTIONS AND HINTS. But first, an example of compile-time and run-time type checking Imagine we have the following code. What.
Functional Programming Lecture 3 - Lists Muffy Calder.
Simon Peyton Jones Microsoft Research September 2015.
Haskell With Go Faster Stripes Neil Mitchell. Catch: Project Overview Catch checks that a Haskell program won’t raise a pattern match error head [] =
Simon Peyton Jones, Stephanie Weirich, Richard Eisenberg, Dimitrios Vytiniotis Microsoft Research University of Pennsylvania April 2016.
GADTs meet their match George Karachalias(Ghent University, Belgium) Tom Schrijvers (KU Leuven, Belgium) Dimitrios Vytiniotis(Microsoft Research Cambridge,
1 Scrapping “Scrap Your Nameplate” Michael D. Adams.
CS5205: Foundation in Programming Languages Type Reconstruction
Advanced Functional Programming 2010
What is a Parser? A parser is a program that analyses a piece of text to determine its syntactic structure  3 means 23+4.
CSE341: Programming Languages Lecture 11 Type Inference
Conditional Expressions
Types CSCE 314 Spring 2016.
C# Operator Overloading and Type Conversions
Class 11: Two-argument recursion
PROGRAMMING IN HASKELL
A lightening tour in 45 minutes
AFP - Lecture 2 Domain Specific Embedded Languages
Lecture 2 Domain Specific Embedded Languages
Subroutines Idea: useful code can be saved and re-used, with different data values Example: Our function to find the largest element of an array might.
PROGRAMMING IN HASKELL
Higher-Order Procedures
Advanced Functional Programming
Extended Static Checking for Haskell (ESC/Haskell)
PROGRAMMING IN HASKELL
CSE341: Programming Languages Lecture 11 Type Inference
CSE341: Programming Languages Lecture 11 Type Inference
Advanced Functional Programming
PROGRAMMING IN HASKELL
CSCE 314: Programming Languages Dr. Dylan Shell
PROGRAMMING IN HASKELL
Records and Type Classes
Grab Bag of Interesting Stuff
CSE341: Programming Languages Lecture 11 Type Inference
Programming Languages
Java’s Central Casting
CSEP505: Programming Languages Lecture 9: Haskell Typeclasses and Monads; Dan Grossman Autumn 2016.
CSE341: Programming Languages Lecture 11 Type Inference
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
Static Contract Checking for Haskell
Records and Type Classes
Presentation transcript:

Microsoft Research University of Pennsylvania A reflection on types Simon Peyton Jones, Stephanie Weirich, Richard Eisenberg, Dimitrios Vytiniotis Microsoft Research University of Pennsylvania August 2016

Programs that are well typed Bad type systems Programs that are well typed All programs Programs that work Zone of Abysmal Pain

Sexy type systems All programs Programs that are well typed Programs that work Smaller Zone of Abysmal Pain

Implementing a store But what is a “Store”?? newSTRef :: a -> ST (STRef a) readSTRef :: STRef a -> ST a writeSTRef :: STRef a -> a -> ST () Let’s implement this in Haskell, as a state transformer newtype ST a = MkST (Store -> (a,Store)) But what is a “Store”??

Promising failures

Implementing a store But what is a “Store”? It maps keys (STRefs) to values of many different types. Attempt 1: use Data.Map newtype STRef a = MkSTRef Int type Store = Map Int ???? lookupStore :: STRef s a -> Store -> Maybe a

Implementing a store Attempt 2: we need dynamic types, even in a statically typed language newtype STRef a = MkSTRef Int type Store = Map Int Dynamic fromDynamic :: Dynamic -> Maybe a lookupStore :: STRef a -> Store -> Maybe a lookupStore (MkSTRef key) store = case Data.Map.lookup key store of Nothing -> Nothing Just d -> fromDynamic d

See “Theorems for Free” Implementing a store Attempt 2: we need dynamic types, even in a statically typed language Too polymorphic! See “Theorems for Free” newtype STRef a = MkSTRef Int type Store = Map Int Dynamic fromDynamic :: Dynamic -> Maybe a lookupStore :: STRef s a -> Store -> Maybe a lookupStore (MkSTRef key) store = case Data.Map.lookup store key of Nothing -> Nothing Just d -> fromDynamic d

Implementing a store Attempt 3: enumerate the types we need data Dynamic = DInt Int | DBool Bool | DPair Dynamic Dynamic | …etc… class Dyn a where fromDynamic :: Dynamic -> Maybe a toDynamic :: a -> Dynamic lookupStore :: Dyn a => STRef a -> Store -> Maybe a lookupStore (MkSTRef key) store = case Data.Map.lookup store key of Nothing -> Nothing Just d -> fromDynamic d

Implementing a store data Dynamic = DInt Int | DBool Bool | DPair Dynamic Dynamic | …etc… class Dyn a where fromDynamic :: Dynamic -> Maybe a toDynamic :: a -> Dynamic instance Dyn Int where fromDynamic (DInt i) = Just i fromDynamic _ = Nothing toDynamic i = DInt i instance Dyn Bool where fromDynamic (DBool b) = Just b fromDynamic _ = Nothing toDynamic b = DBool b

Implementing a store Attempt 3: enumerate the types we need data Dynamic = DInt Int | DBool Bool | DPair Dynamic Dynamic | …etc… Non-starter because it requires a closed world We want readSTRef :: STRef a -> ST a to work for any type ‘a’, including new ones Also: converting to and fro takes a deep traversal instance (Dyn a, Dyn b) => Dyn (a,b) where toDynamic (a,b) = DPair (toDynamic a) (toDynamic b)

Getting closer (GHC )

Runtime representation of the type of the value What’s in a Dynamic? A Dynamic should consist of The value itself A runtime representation of its type data Dynamic where Dyn :: TypeRep -> a -> Dynamic Runtime representation of the type of the value The value itself

Q1: where do TypeReps come from? class Typeable a where typeRep :: a -> TypeRep toDynamic :: Typeable a => a -> Dynamic toDynamic x = Dyn (typeRep x) x data Dynamic where Dyn :: TypeRep -> a -> Dynamic NB proxy argument

The birth of TypeRep

Q2: what can you do with a TypeRep? class Typeable a where typeRep :: a -> TypeRep data Dynamic where Dyn :: TypeRep -> a -> Dynamic fromDynamic :: forall a. Typeable a => Dynamic -> Maybe a fromDynamic (Dyn trx x) | trx == trr = Just x | otherwise = Nothing where trr = typeRep (undefined :: a) Eek!

Q2: what can you do with a TypeRep? class Typeable a where typeRep :: a -> TypeRep data Dynamic where Dyn :: TypeRep -> a -> Dynamic fromDynamic :: a. Typeable a => Dynamic -> Maybe a fromDynamic (Dyn trx x) | trx == trr = Just (unsafeCoerce x) | otherwise = Nothing where trr = typeRep (undefined :: a)

The right way

Type-indexed type reps A Dynamic should consist of The value itself A runtime representation of its type data Dynamic where Dyn :: TypeRep a -> a -> Dynamic Runtime representation of the type of the value The value itself A type-indexed data structure

Q1: where do TypeReps come from? class Typeable a where typeRep :: TypeRep a toDynamic :: Typeable a => a -> Dynamic toDynamic x = Dyn typeRep x data Dynamic where Dyn :: TypeRep a -> a -> Dynamic No proxy argument No deep traversal Instances of Typeable are built in data Wurble a = MkWurble a | … -- Implicitly you get -- instance Typeable a => Typeable (Wurble a)

Q1: where do TypeReps come from? class Typeable a where typeRep :: TypeRep a toDynamic :: Typeable a => a -> Dynamic toDynamic x = Dyn typeRep x newtype Typeable a = MkT (TypeRep a) toDynamic :: Typeable a -> a -> Dynamic toDynamic (MkT tr) x = Dyn tr x data Wurble a = MkWurble a | … -- Implicitly you get -- instance Typeable a => Typeable (Wurble a) $tWurble :: Typeable a -> Typeable (Wurble a) $tWurble (MkT tra) = MkT (mkTrApp “Wurble” [tra])

Q2: what can you do with TypeRep? data Dynamic where Dyn :: TypeRep a -> a -> Dynamic fromDynamic :: b. Typeable b => Dynamic -> Maybe b fromDynamic (Dyn (ra :: TypeRep a) (x::a)) = let rb = typeRep :: TypeRep b in case ra == rb of True -> Just x False -> Nothing

Q2: what can you do with TypeRep? data Dynamic where Dyn :: TypeRep a -> a -> Dynamic fromDynamic :: b. Typeable b => Dynamic -> Maybe b fromDynamic (Dyn (ra :: TypeRep a)) (x::a) = let rb = typeRep :: TypeRep b in case ra == rb of True -> Just x False -> Nothing Type error 2 Just x :: Maybe a, but we need Maybe b Type error 1 ra :: TypeRep a but rb :: TypeRep b

We need type-aware equality! Fix error 1 compares TypeReps with different indices eqT :: TypeRep a -> TypeRep b -> Maybe (a :=: b) data a :=: b where Refl :: a :=: a fromDynamic :: b. Typeable b => Dynamic -> Maybe b fromDynamic (Dyn (ra :: TypeRep a)) (x::a) = let rb = typeRep :: TypeRep b in case ra `eqT` rb of Just Refl -> Just x Nothing -> Nothing Fix error 2 In here we know that a ~ b Refl :: ab. (a~b) => a :=: b

Do notation for the Maybe monad eqT :: TypeRep a -> TypeRep b -> Maybe (a :=: b) data a :=: b where Refl :: a :=: a fromDynamic :: b. Typeable b => Dynamic -> Maybe b fromDynamic (Dyn (ra :: TypeRep a)) (x::a) = do { Refl <- ra `eqT` (typeRep :: TypeRep b) ; return x } Use do-notation (convenience only)

Soundness Soundness: must not be able to forge TypeReps eqT :: TypeRep a -> TypeRep b -> Maybe (a :=: b) Soundness: must not be able to forge TypeReps A (TypeRep a) is a singleton type That is why the Typeable instances are built-in

Dynamic, fromDynamic, toDynamic Where have we got to? ST library Type-safe reflection Small trusted code base Smaller, more re-usable primitive than Dynamic Store Type-safe Dynamic, fromDynamic, toDynamic Data.Map Trusted code base TypeRep a, Typeable a eqT :: TypeRep a -> TypeRep b -> Maybe (a :=: b)

Typeable and TypeRep

cast takes two dictionary arguments Typeable and TypeRep castR :: TypeRep a -> TypeRep b -> a -> Maybe b cast :: (Typeable a, Typeable b) => a -> Maybe b cast takes two dictionary arguments

Typeable and TypeRep castR :: TypeRep a -> TypeRep b -> a -> Maybe b cast :: (Typeable a, Typeable b) => a -> Maybe b castR r1 r2 x = do { Refl <- r1 `eqT` r2 ; return x }

Typeable and TypeRep Do we need both? castR :: TypeRep a -> TypeRep b -> a -> Maybe b cast :: (Typeable a, Typeable b) => a -> Maybe b castR r1 r2 x = do { Refl <- r1 `eqT` r2 ; return x } cast x = castR typeRep typeRep x Do we need both? Yes: sometimes cast is useful, sometimes castR.

Typeable and TypeRep Have a Typeable dictionary, need a TypeRep: easy, just use typeRep! Have a TypeRep, need a Typeable dictionary: not so easy. Need a new primitive function withTypeable: withTypeable :: TypeRep a -> (Typeable a => r) -> r

Decomposing TypeRep

Return Nothing if the argument Dynamic turns out not to be a list What next? dynHead :: Dynamic -> Maybe Dynamic Return Nothing if the argument Dynamic turns out not to be a list

This should be [tx] but it’s actually txs Decomposing TypeRep dynHead :: Dynamic -> Maybe Dynamic dynHead (Dyn (rxs :: TypeRep txs) (xs :: txs)) | …txs looks like [tx]… …rxs looks like [rx]… = Just (Dyn rx (head xs)) | otherwise = Nothing This should be [tx] but it’s actually txs

Decompose rxs into (rl rx) Decomposing TypeRep dynHead :: Dynamic -> Maybe Dynamic dynHead (Dyn (rxs :: TypeRep txs) (xs :: txs)) = do { App rl rx <- splitApp rxs ; Refl <- rl `eqT` (typeRep :: TypeRep []) ; return (Dyn rx (head xs)) } Decompose rxs into (rl rx) Check that rl = []

Decomposing TypeRep dynHead :: Dynamic -> Maybe Dynamic dynHead (Dyn (rxs :: TypeRep txs) (xs :: txs)) = do { App rl rx <- splitApp rxs ; Refl <- rl `eqT` (typeRep :: TypeRep []) ; return (Dyn rx (head xs)) } data AppResult t where App :: TypeRep a -> TypeRep b -> AppResult (a b) splitApp :: TypeRep t -> Maybe (AppResult t)

Kind polymorphism TypeRep :: k. k -> * dynHead :: Dynamic -> Maybe Dynamic dynHead (Dyn (rxs :: TypeRep txs) (xs :: txs)) = do { App rl rx <- splitApp rxs ; Refl <- rl `eqT` (typeRep :: TypeRep []) ; return (Dyn rx (head xs)) } Representation of the list type constructor TypeRep :: k. k -> * Argument can be of any kind

Kind polymorphism data AppResult t where App :: TypeRep a -> TypeRep b -> AppResult (a b) App ::  kr kb (r::kr) (a::kb->kr) (b::kb). (r ~ a b) => TypeRep a -> TypeRep b -> AppResult r dynHead :: Dynamic -> Maybe Dynamic dynHead (Dyn (rxs :: TypeRep txs) (xs :: txs)) = do { App rl rx <- splitApp rxs ; Refl <- rl `eqT` (typeRep :: TypeRep []) ; return (Dyn rx (head xs)) }

New problem! App ::  kr kb (r::kr) (a::kb->kr) (b::kb). (r ~ a b) => TypeRep a -> TypeRep b -> TypeRep r dynHead :: Dynamic -> Maybe Dynamic dynHead (Dyn (rxs :: TypeRep txs) (xs :: txs)) = do { App rl rx <- splitApp rxs ; Refl <- rl `eqT` (typeRep :: TypeRep []) ; return (Dyn rx (head xs)) } rl :: TypeRep (a :: kb->*) TypeRep ([] :: *->*)

New problem! Conclusion: eqT must be herero-kinded App ::  kr kb (r::kr) (a::kb->kr) (b::kb). (r ~ a b) => TypeRep a -> TypeRep b -> TypeRep r dynHead :: Dynamic -> Maybe Dynamic dynHead (Dyn (rxs :: TypeRep txs) (xs :: txs)) = do { App rl rx <- splitApp rxs ; Refl <- rl `eqT` (typeRep :: TypeRep []) ; return (Dyn rx (head xs)) } rl :: TypeRep (a :: kb->*) TypeRep ([] :: *->*) Conclusion: eqT must be herero-kinded

a and b can have different kinds Kind equalities eqT ::  k1 k1 (a::k1) (b::k2). TypeRep a -> TypeRep b -> Maybe (a :=: b) data a :=: b where Refl :: a :=: a Refl ::  k1 k2 (a::k1) (b::k2). (k1 ~ k2, a ~ b) => a :=: b Matching against Refl gives us a kind equality as well as a type equality

Implementing TypeRep

Building TypeReps -- TypeRep :: k. k -> * data TypeRep a where TrApp :: TypeRep a -> TypeRep b -> TypeRep (a b) TrTyCon :: TyCon -> TypeRep a instance (Typeable a, Typeable b) => Typeable (a b) where typeRep = TrApp typeRep typeRep data Wurble a = MkWurble a | … -- Implicitly you get -- instance Typeable Wurble where -- typeRep = TrTyCon (TyCon “mypackage” “M” “Wurble”)

Another new problem! What does (TypeRep (T IO Int)) look like? -- Typeable ::  k. k -> Constraint data T f a = MkT (f a) -- T ::  k. (k -> *) -> k -> * What does (TypeRep (T IO Int)) look like? TrApp (TrApp trT trIO) trInt trIo = TyCon “base” “GHC.IO” “IO” trInt = TyCon “base” “GHC.Num” “Int” trT = TyCon “mypkg” “M” “T” -- ??? But is (TypeRep (T :: (*->*) -> * -> *)) equal to (TypeRep (T :: (Bool -> *) -> Bool -> *))?

Solution Now eqT on a TrTyCon can compare the kinds -- TypeRep :: k. k -> * data TypeRep a where TrApp :: TypeRep a -> TypeRep b -> TypeRep (a b) TrTyCon :: TyCon -> TypeRep k -> TypeRep (a::k) instance (Typeable a, Typeable b) => Typeable (a b) where typeRep = TrApp typeRep typeRep instance Typeable k => Typeable (T :: k) where typeRep = TrTyCon (TyCon “mypkg” “M” “T”) (typeRep :: TypeRep k) Now eqT on a TrTyCon can compare the kinds

Shortcomings

Polymorphic types Can we have (TypeRep (a. [a] -> [a])) to use to make a Dynamic value for polymorphic ‘reverse’? Alas no: that would require impredicative polymorphism and what would the TypeRep look like? and how could you decompose it? I have no idea how to solve this

Where does that leave us? Type-safe reflection, with extensible dynamic types, and a small trusted code base Requires some Heavy Duty Type Artillery Type classes GADTs and local type equalities Kind polymorphism Kind-heterogeneous type equalities Local kind equalities Type support implemented in GHC 8.0 TypeRep library changes in GHC 8.2