{-# LANGUAGE
ExtensionName #-}
at top of file (recommended)-X
ExtensionName (less recommended, except for -XSafe
):set -X
ExtensionName at ghci
prompt (or running ghci
with -X
...)lift
executes actions from underlying transformed monad:class MonadTrans t where
lift :: Monad m => m a -> t m a
Example: State transformer monad, StateT
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
instance MonadTrans (StateT s) where
lift ma = StateT $ \s -> do -- in monad m
a <- ma
return (a, s)
StateT
get
and put
allow you to modify state
get :: (Monad m) => StateT s m s
put :: (Monad m) => s -> StateT s m ()
Example: Haskell equivalent of x++
in C
import Control.Monad.Trans
import Control.Monad.Trans.State
main :: IO ()
main = runStateT go 0 >>= print
where go = do xplusplus >>= lift . print
xplusplus >>= lift . print
xplusplus = do n <- get; put (n + 1); return n
*Main> main
0
1
((),2)
get
and put
StateT
implementationnewtype 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
get :: (Monad m) => StateT s m s
put :: (Monad m) => s -> StateT s m ()
get
and put
StateT
implementationnewtype 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
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)
MonadIO
classclass (Monad m) => MonadIO m where
liftIO :: IO a -> m a
instance MonadIO IO where
liftIO = id
Let's make liftIO
work for StateT
instance (MonadIO m) => MonadIO (StateT s m) where
liftIO = lift . liftIO
Now can write functions that use IO and work in many monads:
myprint :: (Show a, MonadIO m) => a -> m ()
myprint a = liftIO $ print $ show a
MonadIO
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 -- ^^^^^
RecursiveDo
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)
RecursiveDo
helps structure thinking
mfix
on its own is quite usefulmfix
and rec
Create 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 l1
Call 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 myDefaultValue
mfix
Warm-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 x
fixIO
-- IO
Monad fixed pointInternally, lazy IO is implemented by magic unsafeInterleaveIO
unsafeInterleaveIO :: IO a -> IO a
IO
identify function, but defers IO until the thunk forcedDanger--don't try this at home! No longer a functional language
weird :: IO String
weird = do
xxx <- unsafeInterleaveIO $ do putStrLn "Gotcha!"; return []
return $ 'a':'b':'c':xxx
For IO
, mfix = fixIO
:
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
fix
mfix
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 StateT
What 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
return (a, s1)
instance MonadFix m => MonadFix (StateT s m) where
mfix f = StateT $ \s -> mfix $ \ ~(a, _) -> runStateT (f a) s
A 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 a
A 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 convert
Extension 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) => a
And 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 instancesExample: Do something like Show
for String
vs. [a]
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 FlexibleInstances #-}
{-# 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]
OverlappingInstances
module 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"
Show
actually worksshowList
, with a default definition:class Show a where
show :: a -> String
showList :: [a] -> ShowS
showList as = '[' : intercalate ", " (map show as) ++ "]"
-- Note actual implementation more efficient but equivalent
instance (Show a) => Show [a] where
show as = showList as
Show
instance for Char
overrides default showList
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 -- illegal
Every constraint must have a type variable
sym :: Eq Int => Bool -- illegal
liftIO
works from so many monads
StateT
? Make get
/set
methods{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
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 . put
Remember xplusplus
?
xplusplus = do n <- get; put (n + 1); return n
StateT Int IO
monadget
is Num s => StateT Int IO s
s
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 imagination
Since compiler can't infer return type of get
, must type it manually:
xplusplus = do n <- get :: StateT Int IO Int
put (n + 1)
return n
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 m
s
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
instance (Class a a) => Class [a] Bool -- bad: 2 * a > 1 * a
instance (Class a b) => Class [a] Bool -- bad: 1 * b > 0 * b
The assertion has fewer constructors and variables than the head
instance (Class a Int) => Class a Integer -- bad: 2 >= 2
The 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 covered
This 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 = id
Currently must define an instance for every transformer
instance MonadIO m => MonadIO (StateT s m) where liftIO = lift . liftIO
instance MonadIO m => MonadIO (ReaderT t m) where liftIO = lift . liftIO
instance MonadIO m => MonadIO (WriterT w m) 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 . liftIO
We'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 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
*Main> hNot HTrue
HFalse
*Main> hNot HFalse
HTrue
HNot b nb
computes negation of b
at the type levelOverlappingInstances
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 HFalse
TypeEq a a HTrue
is more specific than TypeEq a b c
c
after instance selection using 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 HFalse
TypeEq
TypeEq
is kind of the holy grail of fundeps
TypeEq
, you can program recursively at type level by distinguishing base and recursive cases!OverlappingInstances
...Example: Let's do for MonadState
what we did for MonadIO
-- If t is StateT, then do one thing for (t s m) (base case):
instance (Monad m) => MonadState s (StateT s m) where
get = StateT $ \s -> return (s, s)
put = StateT $ \_ -> return ((), s)
-- If t is not StateT, do something else (recursive case):
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 m
Last extension: TypeOperators
allows infix types starting with ":
"
data a :*: b = Foo a b
type a :+: b = Either a b
Let'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 >>= print
But 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