1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
module Main where
import Control.Monad.Trans.MultiState
import Control.Applicative ( (<$>), (<*>) )
import Control.Monad.Trans ( lift )
import Control.Monad.Writer
{-
Small example showing
1) a MultiState containing a Char and a String,
2) the polymorphic mGet,
3) how to initially put values into the MultiState using withMultiState,
4) the type inference at work - note that there was no need to annotate
combinedPrint
-}
simpleExample :: IO ()
simpleExample = runMultiStateTNil_
$ withMultiState 'H' -- add a Char to the state
$ withMultiState "ello, World!" -- add a String to the state
$ do
-- the monad here is MultiStateT '[String, Char] IO
let combinedPrint = do
c <- mGet
cs <- mGet
-- i <- mGet -- No instance for (Control.Monad.MultiState.ContainsType Int '[])
-- lift $ print $ (i :: Int)
lift $ putStrLn (c:cs)
combinedPrint
mSet 'J' -- we set the Char in the state to 'J'
combinedPrint
-- output:
-- "Hello, World!
-- Jello, World!
-- "
-- and a more complex example:
newtype Account = Account Float
newtype Interest = Interest Float
setAccount :: MonadMultiState Account m => Float -> m ()
setAccount x = mSet (Account x)
getAccount :: MonadMultiState Account m => m Float
getAccount = do
(Account x) <- mGet
return x
modAccount :: MonadMultiState Account m => (Float -> Float) -> m ()
modAccount f = do
(Account x) <- mGet
mSet (Account (f x))
-- wait for a specific time, changing the account according to interest
wait :: ( MonadMultiState Account m
, MonadMultiState Interest m )
=> Float
-> m ()
wait t = do
(Interest i) <- mGet
(Account x) <- mGet
mSet (Account (x*(1+i)**t))
logAccount :: ( MonadWriter [String] m
, MonadMultiState Account m)
=> m ()
logAccount = do
(Account x) <- mGet
tell $ ["account balance = " ++ show x]
accountCalculation :: Writer [String] ()
accountCalculation = runMultiStateTNil_ $ do
tell ["account calculation start"]
-- we cannot use any of the account methods here, because state is empty
-- logAccount
-- -->
-- No instance for (Control.Monad.MultiState.ContainsType Account '[])
withMultiState (Account 0.0) $ do -- state contains an Account.
logAccount
modAccount (+10.0)
logAccount
-- trying to use "wait" here would give type error, like above.
withMultiState (Interest 0.03) $ do -- state now also contains Interest.
wait 10.0 -- we can use wait, because state contains all
-- necessary stuff.
logAccount
modAccount (\x -> x - 10.0)
wait 10.0
logAccount
mSet (Interest 0.00)
wait 10.0
-- we can return back to the environment without interest
-- but the changes to the account are still present
logAccount
-- and we can return to an empty state
tell ["account calculation end"]
main = do
simpleExample
mapM_ putStrLn $ execWriter accountCalculation
-- whatIsNotPossible :: MultiStateT '[String] IO ()
-- whatIsNotPossible = mGet >>= (lift . print) -- type ambiguous
-- another thing that is not directly possible is the restriction to
-- specific values, i.e. a function
-- restrict :: MultiStateT xvalues m a -> MultiStateT yvalues m a
-- where yvalues is a "superset" of xvalues.
--TODO: example with mGetRaw and withMultiStates
|