180 likes | 210 Views
More Monads. The list monad serves as a monad for non deterministic computations. A result [1,2,3] can be seen as the three potential outcomes of a non deterministic computation. [] indicates that the computation fails and no result is produced. instance Monad [] where
E N D
More Monads The list monad serves as a monad for non deterministic computations. A result [1,2,3] can be seen as the three potential outcomes of a non deterministic computation. [] indicates that the computation fails and no result is produced. instance Monad [] where return x = [x] xs >>= f = concat (map f xs) Example: do x <- [1,2,3] [-x,x]
List Monad class Monad m => MonadPlus m where mzero :: m a mplus :: m a -> m a -> m a instance MonadPlus [] where mzero = [] mplus = (++) guard :: (MonadPlus m) => Bool -> m () guard True = return () guard False = mzero do x <- [1..50] guard ('7' `elem` show x) return x
Example: Moves of a knight on a chess board. type KnightPos = (Int,Int) moveKnight :: KnightPos -> [KnightPos] moveKnight (c,r) = do (c',r') <- [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1), (c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)] guard (c' `elem` [1..8] && r' `elem` [1..8]) return (c',r') in3 :: KnightPos -> [KnightPos] in3 start = do first <- moveKnight start second <- moveKnight first moveKnight second canReachIn3 :: KnightPos -> KnightPos -> Bool canReachIn3 start end = end `elem` in3 start
Some useful functions for Monads sequence :: Monad m => [m a] -> m [a] join :: Monad m => m (m a) -> m a filterM :: Monad m => (a -> m Bool) -> [a] -> m [a] What does the following compute? filterM (\x -> [True, False]) [1,2,3]
Writer Monad The Writer monad can be used for logging events during a computation. Consider the following function that computes a result and produces a log entry about the computation. isBigGang :: Int -> (Bool, String) isBigGang x = (x > 9, "Compared gang size to 9. ") Combining results and simulateneously updating the log can be done by The following function: applyLog :: (a,String) -> (a -> (b,String)) -> (b,String) applyLog (x,log) f = let (y,newLog) = f x in (y,log ++ newLog)
Writer Monad The previous example does not depend on the fact that the log is a string. As long as there is an empty log and two log entries can be combined into a single entry this will work, i.e., log entries form a monoid. class Semigroup a => Monoid a where mempty :: a mappend :: a -> a -> a applyLog :: Monoid m => (a,m) -> (a -> (b,m)) -> (b,m) applyLog (x,log) f = let (y,newLog) = f x in (y,log `mappend` newLog)
Writer Monad The Writer Monad can be implemented as: newtype Writer w a = Writer { runWriter :: (a, w) } instance (Monoid w) => Monad (Writer w) where return x = Writer (x, mempty) (Writer (x,v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v') In Haskell WriterT is implemented as a monad transformer and Writer as An instantiation of WriterT with the identity monad. type Writer w = WriterT w Identity
tell :: Monoid w => w -> Writer w () Using the Writer monad the example can be implemented as follows: isBigGang :: Int -> Writer String Bool isBigGang x = do tell "Compared gang size to 9. " return (x > 9) smallGang :: Writer String Int smallGang = do tell "Smallish gang. " return 3 Example: test3 = do n <- smallGang isBigGang n
Using Writer gcd' :: Int -> Int -> Writer [String] Int gcd' a b | b == 0 = do tell ["Finished with " ++ show a] return a | otherwise = do tell [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)] gcd' b (a `mod` b) fst $ runWriter (gcd' 8 3) returns 1.
mapM_ putStrLn $ snd $ runWriter (gcd' 8 3) prints: 8 mod 3 = 2 3 mod 2 = 1 2 mod 1 = 0 Finished with 1 This implementation is fast because it combines the log entry with brackets to the right, i.e., using the following pattern: a ++ (b ++ (c ++ (d ++ (e ++ f))))
Now consider: gcdReverse :: Int -> Int -> Writer [String] Int gcdReverse a b | b == 0 = do tell ["Finished with " ++ show a] return a | otherwise = do result <- gcdReverse b (a `mod` b) tell [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)] return result
mapM_ putStrLn $ snd $ runWriter (gcdReverse 8 3) now prints: Finished with 1 2 mod 1 = 0 3 mod 2 = 1 8 mod 3 = 2 This implementation is slow because it combines the log entry with brackets to the left, i.e., using the following pattern: ((((a ++ b) ++ c) ++ d) ++ e) ++ f
DiffLists newtype DiffList a = DiffList { getDiffList :: [a] -> [a] } toDiffList :: [a] -> DiffList a toDiffList xs = DiffList (xs++) fromDiffList :: DiffList a -> [a] fromDiffList (DiffList f) = f [] instance Monoid (DiffList a) where mempty = DiffList (\xs -> [] ++ xs) (DiffList f) `mappend` (DiffList g) = DiffList (\xs -> f (g xs))
(toDiffList [1,2] `mappend` toDiffList [3,4]) `mappend` toDiffList [5,6] → DiffList (\xs -> ([1,2]++) (([3,4]++) xs)) `mappend` toDiffList [5,6] → DiffList (\xs -> ([1,2]++) (([3,4]++) (([5,6]++) xs))) toDiffList [1,2] `mappend` (toDiffList [3,4]) `mappend` toDiffList [5,6]) → toDiffList [1,2] `mappend` DiffList (\xs -> ([3,4]++) (([5,6]++) xs)) → DiffList (\xs -> ([1,2]++) (([3,4]++) (([5,6]++) xs))) fromDiffList (DiffList (\xs -> ([1,2]++) (([3,4]++) (([5,6]++) xs)))) → (\xs -> ([1,2]++) (([3,4]++) (([5,6]++) xs)))) [] → [1,2]++([3,4]++([5,6]++[]))
Further Features of HaskellRecords data Customer = Customer { customerID :: Int, customerName :: String, customerAddress :: Address } The declaration defines the new data type Customer and its constructor Customer :: Int -> String -> Address -> Customer. In addition it also defines the following functions: customerID :: Customer -> Int customerName :: Customer -> String customerAddress :: Customer -> Address
myCustomer = Customer 1234 "Peter Pan" neverland Or alternatively: myCustomer = Customer { customerID = 1234, customerName = "Peter Pan", customerAddress = neverland }
Concurrency/Threads Creating a thread: forkIO :: IO () -> IO ThreadId Example: import Control.Concurrent (forkIO) import qualified Data.ByteString.Lazy as L import Codec.Compression.GZip (compress) main = do putStr "Enter a file to compress> " name <- getLine if null name then return () else do content <- L.readFile name forkIO (compressFile name content) main where compressFile path = L.writeFile (path ++ ".gz") . compress
Foreign Function Interface import Foreign import Foreign.C.Types foreign import ccall "math.h sin" c_sin :: CDouble -> CDouble Points to consider: • Side-effects in C function • Use monad IO • Thread safe code • Multiple threads are extremely common in Hakell code!