Slide 1

Slide 1 text

ANUPAM JAIN Type generic programming PURESCRIPT - TYPEABLE

Slide 2

Slide 2 text

data Company = Company [Dept] data Dept = Dept Name Manager [SubUnit] data SubUnit = EmpUnit Employee | DeptUnit Dept data Employee = Employee Person Salary data Person = Person Name Address data Salary = Salary Number type Manager = Employee type Name = String type Address = String MOTIVATION increase :: (Salary -> Salary) -> Company -> Company

Slide 3

Slide 3 text

increase :: (Salary->Salary) -> Company -> Company increase k (Company ds) = Company (map (incD k) ds) incD :: (Salary->Salary) -> Dept -> Dept incD k (Dept nm mgr us) = Dept nm (incE k mgr) (map (incU k) us) incU :: (Salary->Salary) -> SubUnit -> SubUnit incU k (EmpUnit e) = EmpUnit (incE k e) incU k (DeptUnit d) = DeptUnit (incD k d) incE :: (Salary->Salary) -> Employee -> Employee incE k (Employee p s) = Employee p (incS k s) incS :: (Salary->Salary) -> Salary -> Salary incS = identity BOILERPLATE

Slide 4

Slide 4 text

increase :: forall a. (Salary -> Salary) -> a -> a DESIRED SOLUTION

Slide 5

Slide 5 text

class MapSalary a where increase ::(Salary -> Salary) -> a -> a instance mapSalary :: MapSalary Salary where increase f a = f a else instance mapSalary :: MapSalary a where increase _ a = a AD HOC OVERLOADING? Too much boilerplate. Need a general solution.

Slide 6

Slide 6 text

increase :: forall a. (Salary -> Salary) -> a -> a increase f a | a `instanceOf` Salary = f a | otherwise = a DESIRED SOLUTION instanceOf :: forall a. a -> Type?? -> Boolean

Slide 7

Slide 7 text

-Proxy a = Proxy -booleanType :: Proxy Boolean booleanType = Proxy -instanceOf :: forall a b. a -> Proxy b -> Boolean instanceOf a Proxy = ??? PROXY DOESN’T SUFFICE

Slide 8

Slide 8 text

-TypeRep a -typeRep :: forall a. TypeRep a -eqTypeRep :: forall a b. TypeRep a -> TypeRep b -> Boolean -instanceOf :: forall a. a -> TypeRep b -> Boolean instanceOf a t = eqTypeRep (typeRep :: _ a) t REIFY TYPES Predefined a == b => TypeRep a == TypeRep b

Slide 9

Slide 9 text

data TypeRep a where TInt :: Type Int TChar :: Type Char TArray :: Type a -> Type (Array a) TFunc :: Type b -> Type c -> Type (Function b c) TTuple :: Type a -> Type b -> Type (Tuple a b) eqTypeRep :: forall a b. TypeRep a -> TypeRep b -> Boolean eqTypeRep TInt TInt = True … MANUALLY - GADTS

Slide 10

Slide 10 text

data TypeRep a = TInt (Int ~ a) | TChar (Char ~ a) | TArray (Type b) (a ~ Array b) | TFunc (Type b) (Type c) (a ~ Function b c) | TTuple (Type b) (Type c) (a ~ (Tuple b c)) eqTypeRep :: forall a b. TypeRep a -> TypeRep b -> Boolean eqTypeRep (TInt w1) (TInt w2) = Just $ symm w1 >>> w2 … MANUALLY - WITHOUT GADTS Not extensible Explained later

Slide 11

Slide 11 text

typeRep :: forall a. TypeRep a ?? class Typeable a where typeRep :: TypeRep a instance Typeable Int where typeRep :: TInt else … CREATING TYPEREPS

Slide 12

Slide 12 text

increase :: forall a. (Salary -> Salary) -> a -> a increase f a | a `instanceOf` typeRep = f a | otherwise = a SOLUTION

Slide 13

Slide 13 text

increase :: forall a. (Salary -> Salary) -> a -> a increase f a | a `instanceOf` typeRep = f a | otherwise = a SOLUTION Cannot match a with Salary

Slide 14

Slide 14 text

increase :: forall a. (Salary -> Salary) -> a -> a increase f a | a `instanceOf` typeRep = f (coerceToSalary a) | otherwise = a coerceToSalary :: forall a. a -> Salary coerceToSalary = unsafeCoerce SOLUTION

Slide 15

Slide 15 text

increase :: forall a. (Salary -> Salary) -> a -> a increase f a = case a `instanceOf` typeRep of Just coercion = f (coercion a) Nothing -> a instanceOf :: forall a. a -> TypeRep b -> Maybe (a -> b) instanceOf ta tb | eqTypeRep ta tb = Just (unsafeCoerce identity) | otherwise = Nothing ENCAPSULATE UNSAFE BEHAVIOUR (TYPE WITNESSES)

Slide 16

Slide 16 text

increase :: forall a. (Salary -> Salary) -> a -> a increase f a = case cast a of Just n = f n Nothing -> a cast :: forall a b. Typeable a => Typeable b => a -> Maybe b cast a = case a `instanceOf` (typeRep :: _ b) of Nothing -> Nothing Just f -> f a EQUIVALENTLY Runtime cast

Slide 17

Slide 17 text

newtype Leibniz a b = Leibniz (forall f. f a -> f b) infix 4 type Leibniz as ~ runLeibniz :: forall f a b. a ~ b -> f a -> f b runLeibniz (Leibniz f) = f LEIBNIZ EQUALITY

Slide 18

Slide 18 text

eqT :: forall a b. TypeRep a -> TypeRep b -> Maybe (a ~ b) eqT ta tb | eqTypeRep ta tb = Just (unsafeCoerce (identity :: Leibniz a a)) | otherwise = Nothing cast :: forall a b. Typeable a => Typeable b => a -> Maybe b cast a = eqT (typeRep :: _ a) (typeRep :: _ b) :: Maybe (Identity a -> Identity b) # map \f -> runLeibniz f (Identity a) :: Maybe (Identity b) # map unwrap :: Maybe b CAST USING LEIBNIZ

Slide 19

Slide 19 text

class Typeable a => Data a where gmapT :: (forall b. Data b => b -> b) -> a -> a instance Data Employee where gmapT f (E per sal) = E (f per) (f sal) instance Data Bool where gmapT f x = x instance Data a => Data [a] where gmapT f [] = [] gmapT f (x:xs) = f x : f xs WRAPPING IT UP

Slide 20

Slide 20 text

everywhere :: Data a => (forall b. Data b => b -> b) -> a -> a everywhere f x = f (gmapT (everywhere f) x) solution :: (Salary->Salary) -> Company -> Company Solution k = everywhere (increase k) NO BOILERPLATE

Slide 21

Slide 21 text

data Dynamic' t a = Dynamic' (TypeRep a) (t a) data Dynamic t = Dynamic (Exists (Dynamic' t)) dynamic :: forall t a. Typeable a => t a -> Dynamic t dynamic a = Dynamic (mkExists (Dynamic' typeRep a)) unwrapDynamic :: forall t a. TypeRep a -> Dynamic t -> Maybe (t a) unwrapDynamic ta (Dynamic e) = e # runExists \(Dynamic' ti v) -> map (\w -> runLeibniz w v) (eqT ti ta) ANOTHER EXAMPLE - DYNAMIC VALUES

Slide 22

Slide 22 text

IMPLEMENTATION DETAILS

Slide 23

Slide 23 text

data Proxy0 (t :: Type) data Proxy1 (t :: Type -> Type) … foreign import proxy0 :: forall t. Proxy0 t foreign import proxy1 :: forall t. Proxy1 t … PROXY

Slide 24

Slide 24 text

class Tag0 a where tag0 :: Proxy0 a class Tag1 (a :: Type -> Type) where tag1 :: Proxy1 a … instance tagInt :: Tag0 Int where tag0 = proxy0 instance tagArray :: Tag1 Array where tag1 = proxy1 … TAGS User Defined Instances

Slide 25

Slide 25 text

instance tag1FromTag2 :: (Tag2 t, Typeable a) => Tag1 (t a) where tag1 = proxy1FromTag2 instance tag0FromTag1 :: (Tag1 t, Typeable a) => Tag0 (t a) where tag0 = proxy0FromTag1 … foreign import proxy1FromTag2 :: forall t a. Tag2 t => Typeable a => Proxy1 (t a) foreign import proxy0FromTag1 :: forall t a. Tag1 t => Typeable a => Proxy0 (t a) … TAG CHAINING A -> B -> [A,B] [A,B] -> C -> [A,B,C]

Slide 26

Slide 26 text

instance Tag0 t => Typeable t where typeRep = typeRepDefault0 foreign import typeRepDefault0 :: forall a. Tag0 a => TypeRep a TYPEABLE A -> A

Slide 27

Slide 27 text

data TypeRow (r :: RL.RowList) foreign import typeRowToTypeRep :: RL.RowToList r rl => TypeRow rl -> TypeRep (Record r) foreign import typeRowNil :: TypeRow RL.Nil foreign import typeRowCons :: SProxy s -> TypeRep t -> TypeRow rs -> TypeRow (RL.Cons s t rs) TYPEABLE FOR ROW TYPES A -> A [] S -> T -> R -> (S,T):R

Slide 28

Slide 28 text

instance(RL.RowToList rs ls, TypeableRecordFields ls) => Typeable (Record rs) where typeRep = typeRowToTypeRep (typeableRecordFields (RLProxy :: _ ls)) TYPEABLE FOR ROW TYPES

Slide 29

Slide 29 text

class TypeableRecordFields rowlist where typeableRecordFields :: RLProxy rowlist -> TypeRow rowlist instance TypeableRecordFields RL.Nil where typeableRecordFields _ = typeRowNil instance TypeableRecordFields (RL.Cons key focus rowlistTail) where typeableRecordFields _ = typeRowCons key (reflectSymbol key) (typeRep :: _ focus) tail where key = SProxy :: _ key tail = typeableRecordFields (RLProxy :: _ rowlistTail) TYPEABLE FOR ROW TYPES