Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Purescript Typeable

9d46cbdc3830248a0615f14ae4b7b33d?s=47 Anupam
January 18, 2021

Purescript Typeable

A talk presented at the Purescript semi-monthly meetup on 18 Jan 2021.

The github repo for purescript-typeable is here - https://github.com/ajnsit/purescript-typeable/

9d46cbdc3830248a0615f14ae4b7b33d?s=128

Anupam

January 18, 2021
Tweet

Transcript

  1. ANUPAM JAIN Type generic programming PURESCRIPT - TYPEABLE

  2. 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
  3. 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
  4. increase :: forall a. (Salary -> Salary) -> a ->

    a DESIRED SOLUTION
  5. 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.
  6. 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
  7. -Proxy a = Proxy -booleanType :: Proxy Boolean booleanType =

    Proxy -instanceOf :: forall a b. a -> Proxy b -> Boolean instanceOf a Proxy = ??? PROXY DOESN’T SUFFICE
  8. -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
  9. 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
  10. 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
  11. typeRep :: forall a. TypeRep a ?? class Typeable a

    where typeRep :: TypeRep a instance Typeable Int where typeRep :: TInt else … CREATING TYPEREPS
  12. increase :: forall a. (Salary -> Salary) -> a ->

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

    a increase f a | a `instanceOf` typeRep = f a | otherwise = a SOLUTION Cannot match a with Salary
  14. 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
  15. 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)
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. IMPLEMENTATION DETAILS

  23. data Proxy0 (t :: Type) data Proxy1 (t :: Type

    -> Type) … foreign import proxy0 :: forall t. Proxy0 t foreign import proxy1 :: forall t. Proxy1 t … PROXY
  24. 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
  25. 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]
  26. instance Tag0 t => Typeable t where typeRep = typeRepDefault0

    foreign import typeRepDefault0 :: forall a. Tag0 a => TypeRep a TYPEABLE A -> A
  27. 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
  28. instance(RL.RowToList rs ls, TypeableRecordFields ls) => Typeable (Record rs) where

    typeRep = typeRowToTypeRep (typeableRecordFields (RLProxy :: _ ls)) TYPEABLE FOR ROW TYPES
  29. 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