Created
March 30, 2016 11:45
-
-
Save jberthold/0f090e20d85abc7a3a4377ce358e3fd4 to your computer and use it in GitHub Desktop.
Chapter "Generic Programming" from Bob Harper, in Haskell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module GenericChapter where | |
-- | We define our own "Polynomial functor" type class. | |
-- Declaring an instance for this type class means to state a rule in | |
-- the 14.1 rule system for "poly" (static semantics). The respective | |
-- implementation of @pfmap@ realises the dynamic semantics (evaluation). | |
class PolyFunc p where | |
pfmap :: (a -> b) -> p a -> p b | |
------------------------------------------------------------ | |
-- | Identity on types, not totally straightforward though. BTW the | |
-- functor instance for @Data.Functor.Identity@ uses @fmap = coerce@ | |
newtype Id a = Id a | |
deriving (Eq, Show) | |
instance PolyFunc Id -- 14.1a: "Id is a poly" | |
where pfmap f (Id x) = let exe' = f x -- 14.3a | |
in Id exe' -- @Id a@ is a newtype, so this | |
-- is actually just @exe'@ | |
------------------------------------------------------------ | |
-- | unit, or to be precise, the constant type function unit. Same | |
-- remark holds here about not being completely straightforward | |
newtype Unit a = Unit () | |
deriving (Eq, Show) | |
instance PolyFunc Unit -- 14.1b | |
where pfmap f (Unit e) = Unit e -- actually represented as e | |
------------------------------------------------------------ | |
-- | Product, with helpers | |
-- The confusing part is that we need functors in the components | |
data Times x y t = Times (x t) (y t) -- angle brackets | |
deriving (Eq, Show) | |
left :: Times x y t -> x t -- "l" suffix | |
left (Times l _) = l | |
right :: Times x y t -> y t -- "r" suffix | |
right (Times _ r) = r | |
-- Now, how do we construct a "normal" pair (a,b) from this? The | |
-- answer is, we cannot, unless a or b is a type constant, or else | |
-- a==b==t; because we only handle type functions of one argument, t. | |
instance (PolyFunc tau1, PolyFunc tau2) -- 14.1c, 2 premises | |
=> PolyFunc (Times tau1 tau2) | |
where pfmap f (Times el er) -- 14.3c | |
= Times (pfmap f el) (pfmap f er) | |
------------------------------------------------------------ | |
-- | Void type function, with no constructors for the result | |
data Void a | |
-- must be derived "stand-alone". Throws exceptions in ghci | |
instance Eq (Void a) where (==) = const (const False) | |
instance Show (Void a) where show x = x `seq` "Wot?" | |
instance PolyFunc Void -- 14.1d | |
where pfmap _ = error "How did we get here?" | |
------------------------------------------------------------ | |
-- | Sum type with two constructors (l and r prefix) | |
data Sum a b t = L (a t) | R (b t) | |
deriving (Eq, Show) | |
instance (PolyFunc tau1, PolyFunc tau2) -- 14.1e, premises | |
=> PolyFunc (Sum tau1 tau2) | |
where pfmap f e | |
= case e of -- 14.3e. Confusing part is | |
L x1 -> L (pfmap f x1) -- that the target type is | |
R x2 -> R (pfmap f x2) -- again the same sum type |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@jberthold You may hit the error for non-terminating arguments of type
Void t
. I think the following is closer to the PFPL version:I'd probably implement
Eq
andShow
similarly.