Programozás burritokkal
Programozás monádokkal – programstruktúrálás
type IO a = World -> (a, World) -- putStr :: String -> IO () -- getLine :: IO String (>>=) :: IO a -> (a -> IO b) -> IO b (f >>= g) w = (g x) w’ where (x, w’) = f w (>>) :: IO a -> IO b -> IO b f >> g = f >>= \_ -> g main :: IO () main =
putStr "Provide me a word> " >> getLine >>= \s ->
if (s == reverse s)
then putStr "A palindrome." else putStr "Not a palindrome."
Programozás monádokkal – a
Monad
típusosztály
{-# LANGUAGE KindSignatures #-} -- Control.Monad
class Monad (m :: * -> *) where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b x >> f = x >>= \_ -> f fail :: String -> m a fail s = error s (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c) f >=> g = \x -> f x >>= g Monádtörvények: return >=> f === f f >=> return === f (f >=> g) >=> h === f >=> (g >=> h)
+
„run” függvény
[5..26]Programozás monádokkal – a
do
szintaktika
Átírási szabályok:
do { e1; e2 } --> e1 >> do { e2 }
do { p <- e1; e2 } --> e1 >>= \x -> case x of p -> do { e2 } _ -> fail "error" do { let p = e1; e2 } --> let p = e1 in do { e2 }
do { e } --> e
Például:
do { do x <- f; x <- f f >>= \x -> y <- g; y <- g g >>= \y -> z <- h; --> z <- h --> h >>= \z ->let t = (x,y,z); let t = (x,y,z) let t = (x,y,z) in
return t return t return t
}
Applicative
...?!
Prelude> :load Identity.hs
[1 of 1] Compiling Main ( Identity.hs, interpreted )
Identity.hs:6:10: Warning:
‘Identity’ is an instance of Monad but not Applicative - this will become an error in GHC 7.10, under the Applicative-Monad Proposal.
Ok, modules loaded: Main.
MindenMonadideális esetbenApplicative Functor:
class Functor (f :: * -> *) where -- Data.Functor fmap :: (a -> b) -> f a -> f b
-- Control.Applicative
class (Functor f) => Applicative (f :: * -> *) where pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b instance (Monad m) => Functor m where
f ‘fmap‘ x = pure f <*> x
instance (Monad m) => Applicative m where
pure x = return x
af <*> ax = do { f <- af; x <- ax; return (f x) }
A
State
monád (intuíció)
type State s a = s -> (a, s)
-- (s -> (a, s)) -> (a -> (s -> (b, s))) -> (s -> (b, s)) (>>=) :: State s a -> (a -> State s b) -> State s b (x >>= f) s = (f x’) s’ where (x’, s’) = x s -- a -> (s -> (a, s)) return :: a -> State s a (return x) s = (x, s) -- s -> (s, s) get :: State s s (get) s = (s, s) -- s -> ((), s) put :: s -> State s () (put x) s = ((), x) -- (s -> (a, s)) -> s -> (a, s)) runState :: State s a -> s -> (a, s)) runState f s = f s
A
State
monád (definíció)
-- Control.Monad.State
data State s a = S (s -> (a, s)) runState :: State s a -> s -> (a, s) runState (S f) x = f x
instance Monad (State s) where return x = S (\s -> (x,s)) x >>= f = S (\s -> let (x’, s’) = runState x s in runState (f x’) s’) get :: State s s get = S (\s -> (s, s)) put :: s -> State s () put x = S (\_ -> ((), x)) [11..26]
A
State
monád (példa)
data Tree a = Node a (Tree a) (Tree a) | Leaf deriving Show
numberTree :: (Eq a) => Tree a -> Tree Int numberTree t = fst (runState (numberTreeM t) [])
numberTreeM :: (Eq a) => Tree a -> State [a] (Tree Int)
numberTreeM Leaf = return Leaf
numberTreeM (Node x lt rt) = do table <- get
let (table’, pos) = case (Data.List.findIndex (== x) table) of Just i -> (table, i)
_ -> (table ++ [x], length table) put table’
lt’ <- numberTreeM lt rt’ <- numberTreeM rt return (Node pos lt’ rt’)
A
Writer
monád (egyszer ˝usített)
-- Control.Monad.Writer data Writer w a = W (a, w)
runWriter :: (Monoid w) => Writer w a -> (a, w) runWriter (W (x, w)) = (x, w)
instance (Monoid w) => Monad (Writer w) where return x = W (x, mempty)
x >>= f = W (let (x’, w) = runWriter x in let (x’’, w’) = runWriter (f x’) in (x’’, w <> w’))
tell :: (Monoid w) => w -> Writer w () tell x = W ((), x)
censor :: (Monoid w) => (w -> w) -> Writer w a -> Writer w a censor f (W (x, w)) = W (x, f w)
A
Writer
monád (
Monoid
)
-- Data.Monoid
class Monoid (a :: *) where
mempty :: a mappend :: a -> a -> a (<>) :: (Monoid a) => a -> a -> a -- GHC 7.6+ (<>) = mappend Monoidtörvények: mempty <> x === x x <> mempty === x x <> (y <> z) === (x <> y) <> z mconcat === foldr (<>) mempty Például:
instance Monoid [a] where
mempty = []
mappend = (++) -- data Sum a = Sum a
instance (Num a) => Monoid (Sum a) where
mempty = Sum 0
(Sum x) ‘mappend‘ (Sum y) = Sum (x + y)
A
Writer
monád (példa)
gcdWithLog :: Int -> Int -> (Int, [String])
gcdWithLog x y = runWriter (censor reverse (gcdWithLogM x y)) gcdWithLogM :: Int -> Int -> Writer [String] Int
gcdWithLogM x y | y == 0 = do tell ["Finished with " ++ show x] return x
gcdWithLogM x y = do
result <- gcdWithLogM y (x ‘mod‘ y)
let msg = show x ++ " mod " ++ show y ++ " = " ++ show (x ‘mod‘ y) tell [msg]
return result
Motiváció – Monádok kombinációja
countEntries :: FilePath -> K () countEntries path = f path 0 where
f p n = do
deepestReached <- get
when (deepestReached < n) (put n)
c <- liftIO (System.Directory.getDirectoryContents p) let contents = filter (‘notElem‘ [".", ".."]) c tell [(p, length contents)]
forM_ contents (\name -> do let newName = p ++ "/" ++ name
isDir <- liftIO (System.Directory.doesDirectoryExist newName) maxDepth <- ask
when (isDir && (n < maxDepth)) (f newName (n + 1))) type S = Int
type C = Int
type K = ReaderT C (StateT S (WriterT [(FilePath,Int)] IO)) runStack :: K a -> Int -> IO ((a,S),[(FilePath,Int)]) runStack k max = runWriterT (runStateT (runReaderT k c) s)
where (s, c) = (0, max)
Monádok rétegelése (stacking)
Részletek – A
MonadTrans
és
MonadIO
osztályok
-- Control.Monad.Trans
class MonadTrans (t :: (* -> *) -> * -> *) where lift :: Monad m => m a -> t m a
Transzformátortörvények:
lift . return === return
lift (x >>= f) === lift x >>= (lift . f)
AzIOesetén ellenben:
-- Control.Monad.IO.Class
class Monad m => MonadIO (m :: * -> *) where liftIO :: IO a -> m a
instance MonadIO IO where liftIO m = m
Adaptált törvények:
liftIO . return === return
liftIO (x >>= f) === liftIO x >>= (liftIO . f)
A
ReaderT
transzformátor (egyszer ˝usített)
{-# LANGUAGE KindSignatures, TypeFamilies, FlexibleContexts #-} class (Monad m) => MonadReader (m :: * -> *) where
type EnvType m -- "associated type (synonym)" ask :: m (EnvType m)
data ReaderT e m a = RT (e -> m a) runReaderT :: ReaderT e m a -> e -> m a runReaderT (RT t) e = t e
type Reader e = ReaderT e Identity runReader :: Reader e a -> e -> a
runReader m x = runIdentity (runReaderT m x) instance (Monad m) => Monad (ReaderT e m) where
return x = lift (return x)
x >>= f = RT (\e -> do x’ <- runReaderT x e runReaderT (f x’) e)
A
ReaderT
transzformátor (egyszer ˝usített, folytatás)
instance MonadTrans (ReaderT e) where lift m = RT (\e -> m)
instance (Monad m) => MonadReader (ReaderT e m) where type EnvType (ReaderT e m) = e
-- ask :: (ReaderT e m) e ask = RT (\e -> return e)
instance (MonadState m) => MonadState (ReaderT e m) where type StateType (ReaderT e m) = StateType m
-- get :: m (StateType m)
get = lift get
-- put :: (StateType m) -> m () put s = lift (put s)
instance (MonadWriter m) => MonadWriter (ReaderT e m) where type WriterType (ReaderT e m) = WriterType m
-- tell :: (WriterType m) -> m ()
tell w = lift (tell w)
-- censor :: (WriterType m -> WriterType m) -> m a -> m a censor f m = RT (\e -> do let m’ = runReaderT m e
censor f m’)
instance (MonadIO m) => MonadIO (ReaderT e m) where liftIO m = lift (liftIO m) [21..26]
A
StateT
transzformátor (egyszer ˝usített)
class (Monad m) => MonadState m where type StateType m
get :: m (StateType m) put :: (StateType m) -> m ()
data StateT s m a = ST (s -> m (a, s)) runStateT :: StateT s m a -> s -> m (a, s) runStateT (ST t) s = t s
type State s = StateT s Identity runState :: State s a -> s -> (a, s) runState m x = runIdentity (runStateT m x) instance (Monad m) => Monad (StateT s m) where
return x = ST (\s -> return (x, s))
x >>= f = ST (\s -> do (x’, s’) <- runStateT x s runStateT (f x’) s’)
A
StateT
transzformátor (egyszer ˝usített, folytatás)
instance MonadTrans (StateT s) where lift m = ST (\s -> do x <- m
return (x, s))
instance (Monad m) => MonadState (StateT s m) where type StateType (StateT s m) = s
get = ST (\s -> return (s,s)) put s = ST (\_ -> return ((),s))
instance (MonadReader m) => MonadReader (StateT s m) where type EnvType (StateT s m) = EnvType m
ask = lift ask
instance (MonadWriter m) => MonadWriter (StateT s m) where type WriterType (StateT s m) = WriterType m
tell w = lift (tell w)
censor f m = ST (\s -> do let m’ = runStateT m s censor f m’)
instance (MonadIO m) => MonadIO (StateT s m) where liftIO m = lift (liftIO m)
A
WriterT
transzformátor (egyszer ˝usített)
class (Monoid (WriterType m), Monad m) => MonadWriter m where type WriterType m
tell :: (WriterType m) -> m ()
censor :: (WriterType m -> WriterType m) -> m a -> m a data WriterT w m a = WT (m (a,w))
runWriterT :: WriterT w m a -> m (a, w) runWriterT (WT m) = m
type Writer a = WriterT a Identity runWriter :: Writer w a -> (a, w) runWriter m = runIdentity (runWriterT m)
instance (Monoid w, Monad m) => Monad (WriterT w m) where return x = WT (return (x, mempty))
x >>= f = WT (do (x’, w) <- runWriterT x (x’’, w’) <- runWriterT (f x’) return (x’’, w <> w’))
A
WriterT
transzformátor (egyszer ˝usített, folytatás)
instance (Monoid w) => MonadTrans (WriterT w) where lift m = WT (do x <- m
return (x, mempty))
instance (Monoid w, MonadState m) => MonadState (WriterT w m) where type StateType (WriterT w m) = StateType m
get = lift get
put s = lift (put s)
instance (Monoid w, MonadReader m) => MonadReader (WriterT w m) where type EnvType (WriterT w m) = EnvType m
ask = lift ask
instance (Monoid w, Monad m) => MonadWriter (WriterT w m) where type WriterType (WriterT w m) = w
tell w = WT (return ((), w))
censor f m = WT (do (x, w) <- runWriterT m return (x, f w))
instance (Monoid w, MonadIO m) => MonadIO (WriterT w m) where liftIO m = lift (liftIO m)
A monádrétegek sorrendje (példa)
stack ::
(MonadState Int m, MonadWriter [String] m, MonadError String m) => m ()
stack = do r <- get
tell ["Hello!"] throwError "Error!"
tell ["Value: " ++ show r] put (r + 1)
type A = ErrorT String (WriterT [String] (State Int)) type B = StateT Int (WriterT [String] (Either String)) stackA :: A ()
stackA = stack stackB :: B () stackB = stack
runA = runState (runWriterT (runErrorT stackA)) 0 runB = runWriterT (runStateT stackB 0)