Let's say you wanted to convert pairs to lists of Strings
pairToStringList :: (Show a, Show b) => (a, b) -> [String]
pairToStringList (a, b) = [show a, show b]
*Main> pairToStringList (True, Just 3)
["True","Just 3"]Now say you want to convert a pair of Enums to a list of Ints
pairToIntList :: (Enum a, Enum b) => (a, b) -> [Int]
pairToIntList (a, b) = [fromEnum a, fromEnum b]Can we generalize this function? Would like to say:
pairToList conv (a, b) = [conv a, conv b]
pairToList show (True, Just 3) -- error
Unfortunately, can't pass methods as arguments, only functions
pairToList :: (a -> b) -> (a, a) -> [b]Let's represent ad hoc polymorphic methods with a class
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
class Function f a b | f a -> b where
funcall :: f -> a -> b
instance Function (a -> b) a b where
funcall = id
pairToList :: (Function f a c, Function f b c) =>
f -> (a, b) -> [c]
pairToList f (a, b) = [funcall f a, funcall f b]Use placeholder singleton types to represent particular methods
data ShowF = ShowF
instance (Show a) => Function ShowF a [Char] where
funcall _ = show
data FromEnumF = FromEnumF
instance (Enum a) => Function FromEnumF a Int where
funcall _ = fromEnumFunction in actionNow singleton types act like method arguments:
*Main> pairToList ShowF (True, 3)
["True","3"]
*Main> pairToList FromEnumF (False, 7)
[0,7]tupleToList for arbitrary n-tuples?
class TupleFoldr f z t r | f z t -> r where
tupleFoldr :: f -> z -> t -> r
-fcontext-stack argumentDeriveDataTypeable extensionShow, Read, Eq, Ord, Bounded, EnumDeriveDataTypeable extension adds two more: Typeable, Data
data MyType = Con1 Int | Con2 String deriving (Typeable, Data)
Data requires that inner types (Int, String) also have instancesTypeable requires any type parameters to have instances-- MyTyCon only typeable when a is
data MyTyCon a = MyTyCon a deriving (Typeable, Data)
Typeable and Data instancesTypeable classimport Data.Typeable to get Typeable class:
class Typeable a where
typeOf :: a -> TypeRep -- Note: never evaluates argument
data TypeRep -- Opaque, but instance of Eq, Ord, Show, TypeableThis allows us to compare types for equality
rtTypeEq :: (Typeable a, Typeable b) => a -> b -> Bool
rtTypeEq a b = typeOf a == typeOf b
*Main> rtTypeEq True False
True
*Main> rtTypeEq True 5
FalseOverlappingInstances?GHC has a function unsafeCoerce
unsafeCoerce :: a -> b
Let's use Typeable to make a safe cast function
cast :: (Typeable a, Typeable b) => a -> Maybe b
cast a = fix $ \ ~(Just b) -> if typeOf a == typeOf b
then Just $ unsafeCoerce a
else Nothing
*Main> cast "hello" :: Maybe String
Just "hello"
*Main> cast "hello" :: Maybe Int
Nothing
typeOf on two different types always returns different TypeRepsderiving (Typeable); SafeHaskell disallows manual instancesTo cast monadic computations, etc., use generalized cast, gcast:
import Data.Maybe (fromJust)
gcast :: (Typeable a, Typeable b) => c a -> Maybe (c b)
gcast ca = mcr
where mcr = if typeOf (unc ca) == typeOf (unc $ fromJust mcr)
then Just $ unsafeCoerce ca
else Nothing
unc :: c x -> x
unc = undefined
*Main> fromJust $ gcast (readFile "/etc/issue") :: IO String
"\nArch Linux \\r (\\n) (\\l)\n\n"
*Main> fromJust $ gcast (readFile "/etc/issue") :: IO Int
*** Exception: Maybe.fromJust: Nothingunc in definition of gcast
typeOf is not strictTypeable b => is like a hidden argument; often use undefined functions with type signatures to unpack types and get dictionariesTypeable: mkT [Boilerplate1]mkT ("make transformation") behaves like id except on one type
mkT :: (Typeable a, Typeable b) => (b -> b) -> a -> aExample:
newtype Salary = Salary Double deriving (Show, Data, Typeable)
raiseSalary :: (Typeable a) => a -> a
raiseSalary = mkT $ \(Salary s) -> Salary (s * 1.04)
*Main> raiseSalary ()
()
*Main> raiseSalary 7
7
*Main> raiseSalary (Salary 7)
Salary 7.28mkT
(->) is Typeable, so Data.Typeable exports:instance (Typeable a, Typeable b) => Typeable (a -> b) where ...mkT :: (Typeable a, Typeable b) => (b -> b) -> a -> a
mkT f a = case cast f of Just g -> g a
Nothing -> a
g is applied to a, so must have type a -> acast f must have type Maybe (a -> a)Typeable dictionary of (b -> b) for argument, and dictionary (a -> a) for return of cast[Jones] has detailed explanation of Haskell's type inference
Note, a fancier implementation could use standard maybe function
maybe :: b -> (a -> b) -> Maybe a -> b
maybe b _ Nothing = b
maybe _ f (Just a) = f a
mkT :: (Typeable a, Typeable b) => (b -> b) -> (a -> a)
mkT f = maybe id id $ cast fTypeable: mkQ [Boilerplate1]Function that computes over one type or returns default val:
mkQ :: (Typeable a, Typeable b) => r -> (b -> r) -> a -> r
mkQ defaultVal fn a = ...
mkQ stands for "make query"Example
salaryVal :: Typeable a => a -> Double
salaryVal = mkQ 0 $ \(Salary s) -> s
*Main> salaryVal ()
0.0
*Main> salaryVal 7
0.0
*Main> salaryVal (Salary 7)
7.0Exercise: implement mkQ
mkQ :: (Typeable a, Typeable b) => r -> (b -> r) -> a -> r
mkQ defaultVal fn a = case cast a of
Just b -> fn b
Nothing -> defaultVal
mkQ :: (Typeable a, Typeable b) => r -> (b -> r) -> a -> r
mkQ defaultVal fn = maybe defaultVal fn . cast
extQmkQ only works for one type
mkQ's output to work on another type [Boilerplate1]extQ :: (Typeable a, Typeable b) =>
(a -> r) -> (b -> r) -> a -> r
extQ q f a = case cast a of
Just b -> f b
Nothing -> q aNow can cascade multiple one-type query functions
myShow :: Typeable a => a -> String
myShow = mkQ "unknown type" (show :: Int -> String)
`extQ` (show :: Bool -> String)
`extQ` (show :: Integer -> String)
`extQ` (const "no floating point" :: Double -> String)
infixl 9 `extQ`)tupleToList at beginning of lecture if tuples contain limited number of typesExistentialQuantification extensionLets you introduce type variables on right side of data declaration
{-# LANGUAGE ExistentialQuantification #-}
data Step s a = Done | Skip !s | Yield !a !s
data Stream a = forall s. Stream (s -> Step s a) !s
Stream a, there exists a type s such that...forall, not exists, to avoid introducing new keywordControl.Exception relies on it)Don't confuse with Rank2Types, where forall means for all types s:
data Stream a = Stream (forall s. s -> Step s a)Contexts on existential variables like hidden dictionary fields
data Showable = forall a. (Show a) => Showable a
instance Show Showable where
show (Showable a) = "Showable " ++ show a
Showable value has both a value of type a, and a dictionary for ShowData.Dynamic has type Dynamic, which can hold anything Typeable
data Dynamic -- opaque type
toDyn :: Typeable a => a -> Dynamic
fromDynamic :: Typeable a => Dynamic -> Maybe aunsafeCoerce to coerce everything to a placeholder Obj typeBut easy to implement safely with ExistentialQuantification:
data Dynamic = forall a. Typeable a => Dynamic a
toDyn :: Typeable a => a -> Dynamic
toDyn = Dynamic
fromDynamic :: Typeable a => Dynamic -> Maybe a
fromDynamic (Dynamic a) = cast aGHC runtime implements primitive, unsafe exceptions
raise# :: a -> b
catch# :: IO a -> (b -> IO a) -> IO a -- slight simplification
b is always same type, otherwise get unsafe coercionControl.Exception implements safe, hierarchical exceptions
raise# and catch# only ever called with one type: SomeExceptionclass (Typeable e, Show e) => Exception e where
toException :: e -> SomeException
toException = SomeException -- default impl
fromException :: SomeException -> Maybe e
fromException (SomeException e) = cast e -- default impl
data SomeException = forall e. Exception e => SomeException e
deriving Typeable -- note use of ExistentialQuantification
instance Show SomeException where
show (SomeException e) = show eclass (Typeable e, Show e) => Exception e where
toException :: e -> SomeException
fromException :: SomeException -> Maybe e
To throw an exception, first convert it to type SomeException
throw :: Exception e => e -> a
throw e = raise# (toException e)To catch an exception, must ensure it matches desired type
-- Define catchX because catch#'s real type more complicated
catchX :: IO a -> (b -> IO a) -> IO a
catchX (IO a) handler = IO $ catch# a (unIO . handler)
catch :: (Exception e) => IO a -> (e -> IO a) -> IO a
catch action handler = catchX action handler'
where handler' se =
maybe (throwIO se) handler $ fromException se
handler makes fromException se use e's Exception dictionaryEasy to add your own top-level exception type
data MyException = MyException deriving (Show, Typeable)
instance Exception MyException -- use default methodsBut you can also create a hierarchy of exception types
data AppError = forall e. Exception e => AppError e
deriving (Typeable)
instance Show AppError where show (AppError e) = show e
instance Exception AppError
data Error1 = Error1 deriving (Show, Typeable)
instance Exception Error1 where
toException = toException . AppError
fromException se = do -- using Maybe as a Monad here
AppError e <- fromException se
cast e
-- Now can do the same for Error2, and catch both as AppError
Error1, or any AppErrorData classclass Typeable a => Data a where ...
Data class allows generic traversal and construction of data structures
gfoldl and gunfold methods like thisdata T a b = C1 a b | C2 deriving (Typeable, Data)
gfoldl k z (C1 a b) = z C1 `k` a `k` b
gfoldl k z C2 = z C2
toConstr (C1 _ _) = ... -- encodes constructor number
toConstr C2 = ...
gunfold k z c = case constrIndex c of
1 -> k (k (z C1))
2 -> z C2Data and Typeable [fundamental]Alternative: push generic programming to compile time [Magalhães]
High-level idea: Say you auto-derived instances of Show-like class:
class MyShow a where myShow :: a -> String
instance MyShow MyType where myShow = genericMyShow
MetaData class for which compiler can generate instanceclass MetaData d m | d -> m, m -> d where -- not what GHC does
fromData :: d -> m
toData :: m -> d
MyShow-specific meta-class, such that?class MetaMyShow a where metaMyShow :: a -> String
genericMyShow :: (MetaData d m, MetaMyShow m) => d -> String
genericMyShow = metaMyShow . fromDataDefaultSignatures extensionWe can do even better using DefaultSignatures extension
Allows default methods that don't work for all instances
{-# LANGUAGE DefaultSignatures #-}
class MyShow a where
myShow :: a -> String
default myShow :: (MetaData a m, MetaMyShow m) => a -> String
myShow = genericMyShow
instance MyShow MyType
MetaData class
DeriveGeneric extensionCompiler supports single Generic class that converts any datatype to a Rep that can be computed over generically:
{-# LANGUAGE TypeFamilies #-}
class Generic a where
type Rep a :: * -> *
from :: a -> Rep a x
to :: Rep a x -> atype Rep uses extension called TypeFamilies. Can read above as:
class Generic a rep | a -> rep where
from :: a -> rep x
to :: rep x -> aFunctor-like instances)Rep of a unit type{-# LANGUAGE DeriveGeneric, TypeFamilies, TypeOperators,
FlexibleInstances, FlexibleContexts, UndecidableInstances #-}
import GHC.Generics
data X = X -- because we are dealing with types of kind * -> *
undef2 :: mi c f p -> f p
undef2 _ = undefined
-- A unit type has one constructor and no arguments
data T1 = C1 deriving (Show, Generic)
*Main> :t from C1
from C1 :: Rep T1 x
*Main> :t (undefined :: Rep T1 X)
(undefined :: Rep T1 X) :: D1 Main.D1T1 (C1 Main.C1_0T1 U1) X
*Main> datatypeName (from C1)
"T1"
*Main> moduleName (from C1)
"Main"
*Main> conName $ undef2 (from C1)
"C1"
GHC.Generics contents (part 1){-# LANGUAGE TypeFamilies, KindSignatures, TypeOperators #-}
-- | Unit: used for constructors without arguments
data U1 p = U1
-- | Meta-information (constructor names, etc.)
newtype M1 i c f p = M1 { unM1 :: f p }
-- | Three flavors of meta-information for variable i
data D; type D1 = M1 D -- c instance of Datatype, f is C1 (or :+:)
data C; type C1 = M1 C -- c instance of Constructor, f is S1 (or :*:)
data S; type S1 = M1 S -- c instance of Selector, f is U1 (or Rec0)
class Datatype d where
datatypeName :: t d (f :: * -> *) a -> String
moduleName :: t d (f :: * -> *) a -> String
class Constructor c where
conName :: t c (f :: * -> *) a -> String
class Selector s where
selName :: t s (f :: * -> *) a -> String
data T2 = C2 { t2a :: Bool } deriving (Show, Generic)
data T3 = C3 { t3a :: Bool, t3b :: Bool } deriving (Show, Generic)
*Main> :t (undefined :: Rep T2 X)
(undefined :: Rep T2 X)
:: D1 Main.D1T2 (C1 Main.C1_0T2 (S1 Main.S1_0_0T2 (Rec0 Bool))) X
*Main> -- This was U1 for type T1 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*Main> conName (undef2 $ from $ C2 True)
"C2"
*Main> selName (undef2 $ undef2 $ from $ C2 True)
"t2a"
*Main> :t (undefined :: Rep T3 X)
(undefined :: Rep T3 X)
:: D1
Main.D1T3
(C1
Main.C1_0T3
(S1 Main.S1_0_0T3 (Rec0 Bool) :*: S1 Main.S1_0_1T3 (Rec0 Bool)))
X
GHC.Generics contents (part 2)-- Used to glue multiple constructor arguments together
data (:*:) f g p = f p :*: g p
infixr 6 :*:
-- Used to represent a type with multiple constructors
data (:+:) f g p = L1 { unL1 :: f p } | R1 { unR1 :: g p }
infixr 5 :+:
-- Used to hold actual concrete values of constructor arguments
newtype K1 i c p = K1 { unK1 :: c }
type Rec0 = K1 R
-- From two slides ago:
data U1 p = U1 -- Unit constructors (no arguments)
newtype M1 i c f p = M1 { unM1 :: f p }
data D; type D1 = M1 D -- c instance of Datatype, f is C1 or :+:
data C; type C1 = M1 C -- c instance of Constructor, f is S1 or :*:
data S; type S1 = M1 S -- c instance of Selector, f is U1 or Rec0
p (there to make types of kind ∗ → ∗)M1 exists so a single traversal method can skip over D1, C1, and S1newtype Rec0 c p = K1 c, but some instances use K1 PGeneric instance look like?data T a b = C1 a b | C2 deriving (Show, Generic)
data T_
instance Datatype T_ where
datatypeName _ = "T"
moduleName _ = "Main"
data T_C1_
data T_C2_
instance Constructor T_C1_ where conName _ = "C1"
instance Constructor T_C2_ where conName _ = "C2"
type Rep0T_ a_0 b_1 = D1 T_
(C1 T_C1_ (S1 NoSelector (Rec0 a_0) :*: S1 NoSelector (Rec0 b_1))
:+: (C1 T_C2_ U1))
instance Generic (T a_0 b_1) where
type Rep (T a_0 b_1) = Rep0T_ a_0 b_1
from (C1 f0 f1) = M1 (L1 (M1 (M1 (K1 f0) :*: M1 (K1 f1))))
from (C2) = M1 (R1 (M1 U1))
to (M1 (L1 (M1 (M1 (K1 f0) :*: M1 (K1 f1))))) = C1 f0 f1
to (M1 (R1 (M1 U1))) = C2
Say we are defining our own Show-like class
class MyShow a where myShow :: a -> String
instance MyShow [Char] where myShow = show
instance MyShow Int where myShow = showShow1 to deal with annoying p parameters{-# LANGUAGE FlexibleInstances, UndecidableInstances,
OverlappingInstances, TypeSynonymInstances, TypeOperators,
TypeFamilies, TemplateHaskell, FlexibleContexts #-}
class MyShow1 f where myShow1 :: f p -> String
instance (MyShow1 f) => MyShow1 (M1 i c f) where -- for D1, S1
myShow1 m1 = myShow1 (unM1 m1)
instance (MyShow1 f, MyShow1 g) => MyShow1 (f :+: g) where
myShow1 (L1 a) = myShow1 a
myShow1 (R1 a) = myShow1 aMyShow1When we hit a constructor, want to print the name
instance (Constructor c, MyShow1 f) => MyShow1 (C1 c f) where
myShow1 m1 = conName m1 ++ myShow1 (unM1 m1)
M1 instanceWhen we have no constructor args, don't show anything
instance MyShow1 U1 where myShow1 _ = ""When we have multiple constructor args, show them all
instance (MyShow1 f, MyShow1 g) => MyShow1 (f :*: g) where
myShow1 (fp :*: gp) = myShow1 fp ++ myShow1 gpWhen you hit the actual value, show it
instance (MyShow c) => MyShow1 (K1 i c) where
myShow1 k1 = ' ' : myShow (unK1 k1)
myShow, which we haven't yet defined for many typesMyShowNow can define generic MyShow in terms of MyShow1
instance (Generic a, MyShow1 (Rep a)) => MyShow a where
myShow a = myShow1 $ from aOverlappingInstances?
D1, S1 instances of Show1 (easy)myShowDefault, thenmyShowDefault :: (Generic a, MyShow1 (Rep a)) => a -> String
myShowDefault a = myShow1 $ from a
instance MyShow T1 where myShow = myShowDefault
instance MyShow T2 where myShow = myShowDefault
instance MyShow T3 where myShow = myShowDefault
...
[Char] vs. [a]