User Tools

Site Tools


codesnippets:monads

Monads

Example 1a

  • this example shows how alternative results (sum type, algebraic type, see type A, B and C in the example) can be handled and propagated in a computation by means of 'case' / 'of' constructions
  • it works but it is quite some code
  • Code:
    main :: IO ()
    main = 
        do
            putStrLn $ show (fAListToC [(A 12), (A 7), (A 11), (A 5)] fAToB)
            putStrLn $ show (fAListToC [(A 7), (A 13), (A 11), (A 5)] fAToB)
            putStrLn $ show (fAListToC [(A (-1)), (A 7), (A 11), (A 5)] fAToB)
            putStrLn $ show (fAListToC [(A 7), (A 11), (A 5), (A 13)] fAToB)
     
    data A a = A a | AError | ANone
        deriving Show
     
    data B a = B a | BError | BNone
        deriving Show
     
    data C a = C a | CError | CNone
        deriving Show
     
    fAToB :: A Int -> B Int
    fAToB (A n) 
        | n <= 0 = BNone
        | n < 13 = B (23 `div` n)
        | otherwise = BError
    fAToB AError = BError
    fAToB ANone = BNone
     
    fAListToC :: [A a] -> (A a -> B a) -> C [a]
    fAListToC [] _ = C []
    fAListToC (a@(A _):lrAList) fAToB' = 
        do
            case fAToB' a of
                B b -> 
                    let
                        c = (fAListToC lrAList fAToB')
                    in
                        case c of
                            C lb -> C (b : lb)
                            CNone -> CNone
                            CError -> CError
                BError -> CError
                BNone -> CNone
    fAListToC (AError:_) _ = CError
    fAListToC (ANone:_) _ = CNone
  • Output:
    C [1,3,2,4]
    CError
    CNone
    CError

Example 1b

  • this example does not explain monads but an alternative solution where monads usually apply
  • it shows how alternative results (sum type, algebraic type, see type A, B and C in the example) can be handled and propagated in a computation by means of a function that handles such alternative results
  • that function handles such results by function 'ifC' and makes the job for a 'Monad' of type 'C'
  • more detailed description of the program
    • the function 'fAListToC' takes a list of 'A's (e.g. '[(A 12), (A 7), (A 11), (A 5)]') and transforms it to 'C' with a list of transformed data
    • the transformation of data is according to function 'fAToB'
    • 'fAToB' divides 23 by the number to be transformed, unless the number is 13 or greater then an error propagates ('BError'), and unless the number is 0 or smaler then no data propagates ('BNone')
    • in 'fAListToC' 'BError' propagates to 'CError' and 'BNone' propagates to 'CNone'
  • this example compile warning free with GHC 9.2.7 using -Wall
  • Code:
    main :: IO ()
    main = 
        do
            putStrLn $ show (fAListToC [(A 12), (A 7), (A 11), (A 5)] fAToB)
            putStrLn $ show (fAListToC [(A 7), (A 13), (A 11), (A 5)] fAToB)
            putStrLn $ show (fAListToC [(A (-1)), (A 7), (A 11), (A 5)] fAToB)
            putStrLn $ show (fAListToC [(A 7), (A 11), (A 5), (A 13)] fAToB)
     
    data A a = A a | AError | ANone
        deriving Show
     
    data B a = B a | BError | BNone
        deriving Show
     
    data C a = C a | CError | CNone
        deriving Show
     
    fAToB :: A Int -> B Int
    fAToB (A n) 
        | n <= 0 = BNone
        | n < 13 = B (23 `div` n)
        | otherwise = BError
    fAToB AError = BError
    fAToB ANone = BNone
     
    fAListToC :: [A a] -> (A a -> B a) -> C [a]
    fAListToC [] _ = C []
    fAListToC (a@(A _):lrAList) fAToB' = 
        do
            case fAToB' a of
                B b -> ifC (fAListToC lrAList fAToB' ) (\lb -> C (b : lb)) CNone CError
                BError -> CError
                BNone -> CNone
    fAListToC (AError:_) _ = CError
    fAListToC (ANone:_) _ = CNone
     
    ifC :: C a -> (a -> b) -> b -> b -> b
    ifC (C x) fThen _ _ = fThen x
    ifC CNone _ bNone _ = bNone
    ifC CError _ _ bError = bError
  • Output:
    C [1,3,2,4]
    CError
    CNone
    CError

Example 1c

  • this example uses the instances for Functor, Applicative and Monad
  • this way the operator '»=' can be used
  • Code:
    import qualified Control.Monad as M (liftM, ap)
     
    instance Functor C where
        fmap = M.liftM
     
    instance Applicative C where
        pure = C
        (<*>) = M.ap
     
    instance Monad C where
        CError  >>= _   = CError
        CNone   >>= _   = CNone
        (C x)   >>= f   = f x
        return          =  pure
     
    main :: IO ()
    main = 
        do
            putStrLn $ show (fAListToC [(A 12), (A 7), (A 11), (A 5)] fAToB)
            putStrLn $ show (fAListToC [(A 7), (A 13), (A 11), (A 5)] fAToB)
            putStrLn $ show (fAListToC [(A (-1)), (A 7), (A 11), (A 5)] fAToB)
            putStrLn $ show (fAListToC [(A 7), (A 11), (A 5), (A 13)] fAToB)
     
    data A a = A a | AError | ANone
        deriving Show
     
    data B a = B a | BError | BNone
        deriving Show
     
    data C a = C a | CError | CNone
        deriving Show
     
    fAToB :: A Int -> B Int
    fAToB (A n) 
        | n <= 0 = BNone
        | n < 13 = B (23 `div` n)
        | otherwise = BError
    fAToB AError = BError
    fAToB ANone = BNone
     
    fAListToC :: [A a] -> (A a -> B a) -> C [a]
    fAListToC [] _ = C []
    fAListToC (a@(A _):lrAList) fAToB' = 
        do
            case fAToB' a of
                B b -> fAListToC lrAList fAToB' >>= \lb -> return (b : lb)
                BError -> CError
                BNone -> CNone
    fAListToC (AError:_) _ = CError
    fAListToC (ANone:_) _ = CNone
  • Output:
    C [1,3,2,4]
    CError
    CNone
    CError

Example 2

  • Code:
    module Main where
     
    -- import liftM, and ap to create a Tray monad
    import qualified Control.Monad as M (liftM, ap)
     
    main :: IO ()
    main = do
        -- execute the assemply line and print the result starting with a log that is 300 mm in diameter, and 250 mm long
        print $ assemblyLine (Log 300 250)
        -- the same with other diameter, and length
        print $ assemblyLine (Log 200 250)
        -- the same with other diameter, and length
        print $ assemblyLine (Log 24 250)
        -- the same with other diameter, and length
        print $ assemblyLine (Log 250 99)
        -- the same with other diameter, and length
        print $ assemblyLine (Log 22 201)
        -- the same with other diameter, and length
        print $ assemblyLine (Log 29 201)
     
    -- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv --
    -- the analogy of an assambly line
    assemblyLine :: Wood -> Tray Packed
    assemblyLine w = 
        do
            c   <- makeChopsticks w    -- Wood to Chopsticks
            c'  <- polishChopsticks c  -- ...to polished Chopsticks
            c'' <- wrapChopsticks c'   -- ...to packed Chopsticks
            return c''
    -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --
     
    --------------------------------------------------------------------------------
    --  Basic types
     
    --------------------------------------------------------------------------------
    --  Wood
     
    data Wood = Log { nDiameterInMM :: Float,  nLengthInMM :: Float }
        deriving Show
     
    --------------------------------------------------------------------------------
    --  Chopsticks and its Quality
     
    data Quality =  Sawn | Polished
        deriving Show
     
    data Chopsticks = Chopsticks { nPieces :: Integer, nQuality :: Quality }
        deriving Show
     
    --------------------------------------------------------------------------------
    --  Packed with Wrapper or in a StorageBin
     
    data Packed = Wrapper Chopsticks | StorageBin Chopsticks
        deriving Show
     
    --------------------------------------------------------------------------------
    --  worker functions of the basic types
    -- NOTE: All of them can have different in put, but the output is always a Tray.
    -- NOTE: See the data type Tray.
     
    -- makes roughly shaped chopsticks out of wood
    makeChopsticks :: Wood -> Tray Chopsticks
    makeChopsticks w 
        | nGain > 0 = Contains (Chopsticks nGain Sawn)
        | (nLengthInMM w) < 200 = Empty "to short" 
        | otherwise = Empty "to small in diameter"
        where
             nGain 
                | (nLengthInMM w) >= 200 = (round ((((nDiameterInMM w) - 20) * (nLengthInMM w)) / (220 * 5)))
                | otherwise = 0
     
    -- polishes chopsticks
    polishChopsticks :: Chopsticks -> Tray Chopsticks
    polishChopsticks c 
        | amount >= 1 = Contains (Chopsticks amount Polished)
        | otherwise = Empty "no chopsticks after polishing"
        where
            amount = floor (((fromIntegral (nPieces c)) :: Double) * 0.9)
     
    -- wraps chopsticks
    wrapChopsticks :: Chopsticks -> Tray Packed
    wrapChopsticks c 
        | amount >= 50 = Contains (Wrapper (Chopsticks amount (nQuality c)))
        | amount <= 0 = Empty "no chopsticks at packing"
        | otherwise = Contains (StorageBin (Chopsticks amount (nQuality c)))
        where
            amount = floor (((fromIntegral (nPieces c)) :: Double) * 0.95)
     
     
    --------------------------------------------------------------------------------
    --  Tray
     
    -- trays are either empty or contain a single item
    data Tray x = Empty String | Contains x
        deriving Show
     
    --------------------------------------------------------------------------------
    --  Tray as instance of Monad
     
    -- Tray is a monad
    instance Functor Tray where
        fmap = M.liftM
     
    instance Applicative Tray where
        pure = return
        (<*>) = M.ap
     
    instance Monad Tray where
        Empty s       >>= _      = Empty s
        (Contains x) >>= worker = worker x
        return                  = Contains
  • Output:
    Contains (Wrapper (Chopsticks {nPieces = 54, nQuality = Polished}))
    Contains (StorageBin (Chopsticks {nPieces = 34, nQuality = Polished}))
    Empty "no chopsticks after polishing"
    Empty "to short"
    Empty "to small in diameter"
    Empty "no chopsticks at packing"
This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
You could leave a comment if you were logged in.
codesnippets/monads.txt · Last modified: by 127.0.0.1

Except where otherwise noted, content on this wiki is licensed under the following license: CC0 1.0 Universal
CC0 1.0 Universal Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki