Providence Salumu
-XExtensionName{-# LANGUAGE ExtensionName #-} pragma at top of fileMethod lift executes actions from underlying transformed monad:
class MonadTrans t where lift :: Monad m => m a -> t m aNote monads have kind ∗ → ∗, so transformers have kind (∗ → ∗) → ∗ → ∗
StateT
State from old lecture--can combine, e.g., state and IOnewtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
instance (Monad m) => Monad (StateT s m) where
return a = StateT $ \s -> return (a, s)
m >>= k = StateT $ \s0 -> do -- in monad m
~(a, s1) <- runStateT m s0
runStateT (k a) s1
instance MonadTrans (StateT s) where
lift ma = StateT $ \s -> do -- in monad m
a <- ma
return (a, s)StateTLike State, need actions for getting and setting state
get :: (Monad m) => StateT s m s
get = StateT $ \s -> return (s, s)
put :: (Monad m) => s -> StateT s m ()
put s = StateT $ \_ -> return ((), s)Example: count lines of standard input
import Control.Exception
import Control.Monad.Trans
import Control.Monad.Trans.State
countLines :: IO Int
countLines = runStateT go 0 >>= return . fst
where go = lift (try getLine) >>= doline
doline (Left (SomeException _)) = get
doline (Right _) = do n <- get; put (n + 1); go
try getLine is an IO action, execute with liftIO are get, set actions from StateT Int IO monadMonadIORecall the MonadIO class from the iteratee lecture
class (Monad m) => MonadIO m where
liftIO :: IO a -> m a
instance MonadIO IO where
liftIO = idLet's make liftIO work for StateT
instance (MonadIO m) => MonadIO (StateT s m) where
liftIO = lift . liftIONow can execute IO actions without regard to which monad you are in
go = liftIO (try getLine) >>= dolineMonadIO
ContT, ErrorT, ListT, RWST, ReaderT, StateT, WriterT, ...Top-level, let, and where bindings are all recursive in Haskell, e.g.:
oneTwo :: (Int, Int)
oneTwo = (fst y, snd x)
where x = (1, snd y) -- mutual recursion
y = (fst x, 2)
nthFib :: Int -> Integer
nthFib n = fibList !! n
where fibList = 1 : 1 : zipWith (+) fibList (tail fibList)Function fix calls a function with its own result, use to re-implement above:
fix :: (a -> a) -> a
fix f = let x = f x in x
oneTwo' :: (Int, Int)
oneTwo' = (fst y, snd x)
where (x, y) = fix $ \ ~(x0, y0) -> let x1 = (1, snd y0)
y1 = (fst x0, 2)
in (x1, y1)
nthFib' n = fibList !! n
where fibList = fix $ \l -> 1 : 1 : zipWith (+) l (tail l)By contrast, monadic bindings are not recursive
do fibList <- return $ 1 : 1 : zipWith (+) fibList (tail fibList)
... -- error, fibList not in scope ^^^^^^^ ^^^^^^^But monads in the MonadFix class have a fixed-point combinator
class Monad m => MonadFix m where
mfix :: (a -> m a) -> m a
mfix can be used to implement recursive monadic bindings [Erkök00], e.g.:mfib :: (MonadFix m) => Int -> m Integer
mfib n = do
fibList <- mfix $ \l -> return $ 1 : 1 : zipWith (+) l (tail l)
return $ fibList !! n -- ^^^^^DoRec extensionrec keyword introduces recursive bindings in a do block [Erkök02]
MonadFix (rec desugars to mfix calls)oneTwo'' :: (MonadFix m) => m (Int, Int)
oneTwo'' = do
rec x <- return (1, snd y)
y <- return (fst x, 2)
return (fst y, snd x)
oneTwo''' :: (MonadFix m) => m (Int, Int)
oneTwo''' = do
(x, y) <- mfix $ \ ~(x0, y0) -> do x1 <- return (1, snd y0)
y1 <- return (fst x0, 2)
return (x1, y1)
return (fst y, snd x)DoRec helps structure thinking, not shipped that much
mfix on its own is quite usefulmfix and recCreate recursive data structures in one shot
data Link a = Link !a !(MVar (Link a)) -- note ! is okay
mkCycle :: IO (MVar (Link Int))
mkCycle = do
rec l1 <- newMVar $ Link 1 l2 -- but $! would diverge
l2 <- newMVar $ Link 2 l1
return l1Call non-strict methods of classes (easy access to return-type dictionary)
class MyClass t where
myTypeName :: t -> String -- non-strict in argument
myDefaultValue :: t
instance MyClass Int where
myTypeName _ = "Int"
myDefaultValue = 0
getVal :: (MyClass t) => IO t
getVal = mfix $ \t -> do -- doesn't use mfix's full power
putStrLn $ "Caller wants type " ++ myTypeName t
return myDefaultValuemfixWarm-up: The Identity monad
newtype Identity a = Identity { runIdentity :: a }
instance Monad Identity where
return = Identity
m >>= k = k (runIdentity m)
newtype compiles to nothing, so basically same as fix:instance MonadFix Identity where
mfix f = let x = f (runIdentity x) in xFor IO, mfix = fixIO (recalling IORef from laziness lecture):
fixIO :: (a -> IO a) -> IO a
fixIO k = do
ref <- newIORef (throw NonTermination)
ans <- unsafeInterleaveIO (readIORef ref)
result <- k ans
writeIORef ref result
return result
fixmfix is not possibleWhat if we tried to define an mfix-like function for all monads?
mbroken :: (Monad m) => (a -> m a) -> m a -- equivalent to mfix?
mbroken f = fix (>>= f)
mbroken f = mbroken f >>= f
>>= is strict in its first argument for many monads, so*Main> mfix $ const (return 0)
0
*Main> mbroken $ const (return 0)
*** Exception: stack overflow
mfix needs to take fixed point over value, not over monadic action
ContT, ListT)MonadFix instance for StateTWhat about the StateT monad?
newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
instance (Monad m) => Monad (StateT s m) where
return a = StateT $ \s -> return (a, s)
m >>= k = StateT $ \s0 -> do -- in monad m
~(a, s1) <- runStateT m s0
runStateT (k a) s1
rec notationinstance MonadFix m => MonadFix (StateT s m) where
mfix f = StateT $ \s0 -> do -- in monad m
rec ~(a, s1) <- runStateT (f a) s0 -- ~ redundant?
return (a, s1)
instance MonadFix m => MonadFix (StateT s m) where
mfix f = StateT $ \s -> mfix $ \ ~(a, _) -> runStateT (f a) sA Haskell 2010 type class declaration can take the form:
class ClassName var where
methodName :: Type {- where type references var -}
class (SuperClass var) => ClassName var where ...
class (Super1 var, Super2 var) => ClassName var where ...
...
var need not have kind ∗However, the type of each method must mention var and an implicit (Classname var) is added to the context of each method, e.g.:
Prelude> :t return
return :: Monad m => a -> m aA Haskell 2010 instance declaration has the form:
instance [context =>] ClassName (TypeCon v1 ... vk) where ...
v1 ... vk are all variables and all distinct, ruling out, e.g., instance C (a,a) or instance C (Int a) or instance [[a]]MultiParamTypeClasses extensionEnables type classes with multiple parameters, E.g.:
{-# LANGUAGE MultiParamTypeClasses #-}
class Convert a b where convert :: a -> b
instance Convert Int Bool where convert = (/= 0)
instance Convert Int Integer where convert = toInteger
instance (Convert a b) => Convert [a] [b] where
convert = map convertExtension itself is relatively safe, but encourages other extensions
E.g., each method's type must use every type parameter
class MyClass a b where
aDefault :: a -- can never use (without more extensions...)All types (argument and return) must be fully determined
convert 0 :: Bool -- error, 0 has type (Num a) => aAnd the usual instance restrictions still apply
instance Convert Int [Char] where convert = show -- error bad param
[Char]--i.e., ([] Char)--is not a valid instance parameter, would have to be ([] a)FlexibleInstances extension{-# LANGUAGE FlexibleInstances #-}
instance Convert Int [Char] where
convert = show
instance Convert a a where convert a = a
*Main> convert () :: ()
()
*Main> convert ([1,2,3]::[Int]) :: [Integer]
[1,2,3]
*Main> convert ([1,2,3]::[Int]) :: [Int]
<interactive>:1:1:
Overlapping instances for Convert [Int] [Int]
instance Convert a a
instance Convert a b => Convert [a] [b]
OverlappingInstances extension=>) not considered when selecting instancesNeed it to do something like built-in Show instances for String, []
class MyShow a where myShow :: a -> String
instance MyShow Char where myShow = show
instance MyShow Int where myShow = show
instance MyShow [Char] where myShow = id
instance (MyShow a) => MyShow [a] where
myShow [] = "[]"
myShow (x:xs) = "[" ++ myShow x ++ go xs
where go (y:ys) = "," ++ myShow y ++ go ys
go [] = "]"So does enabling OverlappingInstances fix Convert?
What is the most specific instance?
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverlappingInstances #-}
instance Convert a a where ...
instance (Convert a b) => Convert [a] [b] where ...
*Main> convert ([1,2,3]::[Int]) :: [Int]
<interactive>:1:1:
Overlapping instances for Convert [Int] [Int]
instance [overlap ok] Convert a a
instance [overlap ok] Convert a b => Convert [a] [b]
instance Convert [a] [a] where convert = id
*Main> convert ([1,2,3]::[Int]) :: [Int]
[1,2,3]
OverlappingInstancesmodule Help where
class MyShow a where
myshow :: a -> String
instance MyShow a => MyShow [a] where
myshow xs = concatMap myshow xs
showHelp :: MyShow a => [a] -> String
showHelp xs = myshow xs -- doesn't see overlapping instance
module Main where
import Help
data T = MkT
instance MyShow T where
myshow x = "Used generic instance"
instance MyShow [T] where
myshow xs = "Used more specific instance"
main = do { print (myshow [MkT]); print (showHelp [MkT]) }
*Main> main
"Used more specific instance"
"Used generic instance"
FlexibleContexts extensionMultiParamTypeClasses leads to inexpressible types
toInt val = convert val :: Int
toInt? Would like to write:toInt :: (Convert a Int) => a -> Int
(Convert a Int) => is an illegal context, as Int not a type variableFlexibleContexts extension makes the above type legal to write
Each type variable in context must be "reachable" from a type variable in type
(Reachable = explicitly used, or in another constraint with a reachable variable.)
sym :: forall a. Eq a => Int -- illegalEvery constraint must have a type variable
sym :: Eq Int => Bool -- illegalliftIO works from so many monads
StateT? Make get/set methods{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
-- Saw this very briefly in Monad lecture:
class (Monad m) => MonadState s m where
get :: m s
put :: s -> m ()
instance (Monad m) => MonadState s (StateT s m) where
get = StateT $ \s -> return (s, s)
put s = StateT $ \_ -> return ((), s)Now for each other MonadTrans, pass requests down
liftIO. E.g., for ReaderT:instance (MonadIO m) => MonadIO (ReaderT r m) where
liftIO = lift . liftIO
instance (MonadState s m) => MonadState s (ReaderT r m) where
get = lift get
put = lift . putRemember countLines?
doline (Left (SomeException _)) = get
doline (Right _) = do n <- get; put (n + 1); go
StateT Int IO monadget is StateT Int IO ss in order to select an instance of MonadState!For all compiler knows, might be other matching instances, e.g.,
instance MonadState Double (StateT Int IO) where
-- would be legal, but exists only in compiler's imaginationSince compiler can't infer return type of get, must type it manually:
doline (Left (SomeException _)) = get :: StateT Int IO Int
doline (Right _) = do n <- get; put ((n :: Int) + 1); go
FunctionalDependencies extensionOverlappingInstances)
Lets a class declare some parameters to be functions of others
class (Monad m) => MonadState s m | m -> s where
get :: m s
put :: s -> m ()
| m -> s" says can select an instance based on m without considering s, because s is a function of ms for type inferenceOverlappingInstances)instance [context =>] head [where body]
The Paterson Conditions: for each assertion in the context
No type variable has more occurrences in the assertion than in the head
class Class a b | a -> b
instance (Class a a) => Class [a] Bool -- bad: 2 * a => 1 * a
instance (Class a b) => Class [a] Bool -- bad: 1 * b => 0 * bThe assertion has fewer constructors and variables than the head
instance (Class a Int) => Class a Integer -- bad: 2 => 2The Coverage Condition: For each fundep left -> right, the types in right cannot have type variables not mentioned in left
class Class a b | a -> b
instance Class a (Maybe a) -- ok: a "covered" by left
instance Class Int (Maybe b) -- bad: b not covered
instance Class a (Either a b) -- bad: b not coveredThis legal, decidable program will crash your Haskell compiler
crash = f5 ()
where f0 x = (x, x) -- type size 2^{2^0}
f1 x = f0 (f0 x) -- type size 2^{2^1}
f2 x = f1 (f1 x) -- type size 2^{2^2}
f3 x = f2 (f2 x) -- type size 2^{2^3}
f4 x = f3 (f3 x) -- type size 2^{2^4}
f5 x = f4 (f4 x) -- type size 2^{2^5}UndecidableInstances extensionFlexibleContexts when enabledCan increase with -fcontext-stack=n option, e.g.:
{-# OPTIONS_GHC -fcontext-stack=1024 #-}
{-# LANGUAGE UndecidableInstances #-}-ftemplate-depth= optionMonadIO revisitedRecall definition of MonadIO
class (Monad m) => MonadIO m where
liftIO :: IO a -> m a
instance MonadIO IO where
liftIO = idCurrently must define an instance for every transformer
instance MonadIO (StateT s) where liftIO = lift . liftIO
instance MonadIO (ReaderT t) where liftIO = lift . liftIO
instance MonadIO (WriterT w) where liftIO = lift . liftIO
...With UndecidableInstances, one instance can cover all transformers!
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
-- undecidable: assertion Monad (t m) no smaller than head
instance (MonadTrans t, MonadIO m, Monad (t m)) =>
MonadIO (t m) where liftIO = lift . liftIOWe've seen 6 typeclass-related extensions
{-# LANGUAGE MultiParamTypeClasses #-} -- very conservative
{-# LANGUAGE FlexibleInstances #-} -- conservative
{-# LANGUAGE FlexibleContexts #-} -- conservative
{-# LANGUAGE FunctionalDependencies #-} -- frowned upon
{-# LANGUAGE UndecidableInstances #-} -- very frowned upon
{-# LANGUAGE OverlappingInstances #-} -- the most controversial
data Zero = Zero -- Type-level 0
data Succ n = Succ n -- Type-level successor (n + 1)
class NatPlus a b c | a b -> c, a c -> b where
natPlus :: a -> b -> c
natMinus :: c -> a -> b
instance NatPlus Zero a a where
natPlus _ a = a
natMinus a _ = a
-- Note failure of coverage condition below
instance (NatPlus a b c) => NatPlus (Succ a) b (Succ c) where
natPlus (Succ a) b = (Succ (natPlus a b))
natMinus (Succ c) (Succ a) = natMinus c a
NatPlus a b c, then from types Succ a and b we can compute Succ c (computation at type level)data HFalse = HFalse deriving Show
data HTrue = HTrue deriving Show
class HNot a b | a -> b where hnot :: a -> b
instance HNot HFalse HTrue where hnot _ = HTrue
instance HNot HTrue HFalse where hnot _ = HFalse
class HEven a b | a -> b where hEven :: a -> b
instance HEven Zero HTrue where hEven _ = HTrue
instance (HEven n b, HNot b nb) => HEven (Succ n) nb where
hEven (Succ n) = hnot (hEven n)
*Main> hEven Zero
HTrue
*Main> hEven (Succ Zero)
HFalse
*Main> hEven (Succ (Succ Zero))
HTrue
*Main> hEven (Succ (Succ (Succ Zero)))
HFalse
HNot b nb to compute negation of bOverlappingInstances yet, let's start...Can we compute whether two types are equal? First attempt:
class TypeEq a b c | a b -> c where typeEq :: a -> b -> c
instance TypeEq a a HTrue where typeEq _ _ = HTrue
instance TypeEq a b HFalse where typeEq _ _ = HFalse
TypeEq a a HTrue not more specific than TypeEq a b HFalseTypeEq a b cc after instance selection with another fundepclass TypeCast a b | a -> b where typeCast :: a -> b
instance TypeCast a a where typeCast = id
instance TypeEq a a HTrue where typeEq _ _ = HTrue -- as before
instance (TypeCast HFalse c) => TypeEq a b c where
typeEq _ _ = typeCast HFalseTypeEqTypeEq is kind of the holy grail of fundeps
TypeEq, you can distinguish base and recursive casesOverlappingInstances...Example: Let's do for MonadState what we did for MonadIO
instance (Monad m) => MonadState s (StateT s m) where
get :: m s
put :: s -> m ()
instance (MonadTrans t, MonadState s m, Monad (t m)) =>
MonadState s (t m) where
get = lift get
put = lift . put
MonadIO was easier because type IO can't match parameter (t m)StateT s m matches both of above instance headsOverlappingInstances to select first instance for StateT s mLast extension: TypeOperators allows infix types starting with ":"
data a :*: b = Foo a b
type a :+: b = Either a bLet's use an infix constructor to define a heterogeneous list
data HNil = HNil deriving Show
data (:*:) h t = h :*: !t deriving Show
infixr 9 :*:
-- Example:
data A = A deriving Show
data B = B deriving Show
data C = C deriving Show
foo = (A, "Hello") :*: (B, 7) :*: (C, 3.0) :*: HNil
*Main> foo
(A,"Hello") :*: ((B,7) :*: ((C,3.0) :*: HNil))
*Main> :t foo
foo :: (A, [Char]) :*: ((B, Integer) :*: ((C, Double) :*: HNil))
Notice our list consisted of pairs
foo :: (A, [Char]) :*: (B, Integer) :*: (C, Double) :*: HNil
foo = (A, "Hello") :*: (B, 7) :*: (C, 3.0) :*: HNil
class Select k h v | k h -> v where
(.!) :: h -> k -> v
instance Select k ((k, v) :*: t) v where
(.!) ((_, v) :*: _) _ = v
instance (Select k h v) => Select k (kv' :*: h) v where
(.!) (kv' :*: h) k = h .! k
*Main> foo .! A
"Hello"
OverlappingInstances
Can use to implement all sorts of other features (concatenation, etc.)
Heterogeneous can implement object-oriented programming!
returnIO :: a -> IO a
returnIO = return
data GetVal = GetVal deriving Show
data SetVal = SetVal deriving Show
data ClearVal = ClearVal deriving Show
mkVal n self = do
val <- newIORef (n :: Int)
returnIO $ (GetVal, readIORef val)
:*: (SetVal, writeIORef val)
:*: (ClearVal, self .! SetVal $ 0)
:*: HNil
test = do -- prints 7, then 0
x <- mfix $ mkVal 7
x .! GetVal >>= print
x .! ClearVal
x .! GetVal >>= printBut why mfix?
mfix allows you to override methods with inheritance
SetVal messagesmkConstVal n self = do
super <- mkVal n self
returnIO $ (SetVal, const $ return ())
:*: super
test2 = do
x <- mfix $ mkConstVal 7
x .! GetVal >>= print
x .! ClearVal
x .! GetVal >>= print
*Main> test
7
0
*Main> test2
7
7
mkVal's call to SetVal was properly overridden by mkConstVal