Ремонт принтеров, сканнеров, факсов и остальной офисной техники


назад Оглавление вперед




[18]

£ сс

Order is important when combining monads. StateTs (Error e) is different than ErrorTe (State s). The first produces a combined type of s -> Error e (a,s), in which the computation can either return a new state or generate an error. The second combination produces a combined type of s -> (Error e a,s), in which the computation always returns a new state, and the value can be an error or a normal value.

3.5. Anatomy of a monad transformer

In this section, we will take a detailed look at the implementation of one of the more interesting transformers in the standard library, StateT. Studying this transformer will build insight into the transformer mechanism that you can call upon when using monad transformers in your code. You might want to review the section on the State monad before continuing.

3.5.1. Combined monad definition

Just as the State monad was built upon the definition

newtype State s a = State { runState :: (s -> (a,s)) }

the StateT transformer is built upon the definition

newtype StateT s m a = StateT { runStateT :: (s -> m (a,s)) }

State s is an instance of both the Monad class and the MonadState s class, so StateTs m should also be members of the Monad and MonadState s classes. Furthermore, if m is an instance of MonadPlus, StateTs m should also be a member of MonadPlus.

To define StateT s m as a Monad instance:

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)

(StateT x) >>= f = StateT $ \s -> do (v,s)<- x s-

get new value, state

(StateT x) <- return $ f v - apply bound function to get new state transformation fn

apply the state transformation fn to the new state

Compare this to the definition for State s. Our definition of return makes use of the return function of the inner monad, and the binding operator uses a do-block to perform a computation in the inner monad.

We also want to declare all combined monads that use the StateT transformer to be instaces of the MonadState class, so we will have to give definitions for get and put:

instance (Monad m) => MonadState s (StateT s m) where

ФП 02005-03 01

Лист 60

Копиоова Формат


get = StateT $ \s -> return (s,s) put s = StateT $ \ -> return ((),s)

Finally, we want to declare all combined monads in which StateT is used with an instance of MonadPlus to be instances of MonadPlus:

instance (MonadPlus m) => MonadPlus (StateT s m) where mzero = StateT $ \s -> mzero

(StateT xl) "mplus" (StateT x2) = StateT $ \s -> (xl s) "mplus" (x2 s)

3.5.2.Defining the lifting function

The final step to make our monad transformer fully integrated with Haskells monad classes is to make StateTs an instance of the MonadTrans class by providing a lift function:

instance MonadTrans (StateT s) where

lift c = StateT $ \s -> c >>= (\x -> return (x,s))

The lift function creates a StateT state transformation function that binds the computation in the inner monad to a function that packages the result with the input state. The result is that a function that returns a list (i.e., a computation in the List monad) can be lifted into StateT s [], where it becomes a function that returns a StateT (s -> [(a,s)]). That is, the lifted computation produces multiple (value,state) pairs from its input state. The effect of this is to "fork" the computation in StateT, creating a different branch of the computation for each value in the list returned by the lifted function. Of course, applying StateT to a different monad will produce different semantics for the lift function.

3.5.3.Functors

We have examined the implementation of one monad transformer above, and it was stated earlier that there was no magic formula to produce transformer versions of monads. Each transformers implementation will depend on the nature of the computational effects it is adding to the inner monad.

Despite this, there is some theoretical foundation to the theory of monad transformers. Certain transformers can be grouped according to how they use the inner monad, and the transformers within each group can be derived using monadic functions and functors. Functors, roughly, are types which support a mapping operation fmap :: (a->b) -> f a -> f b. To learn more about it, check out Mark Jones influential paper that inspired the Haskell monad template library.

3.6. More examples with monad transformers

At this point, you should know everything you need to begin using monads and monad transformers in your programs. The best way to build proficiency is to work on actual code. As

ФП 02005-03 01

Лист 61

№ докум.

Копиоова Фоомат


your monadic programs become more abitious, you may find it awkward to mix additional transformers into your combined monads. This will be addressed in the next section, but first you should master the basic process of applying a single transformer to a base monad.

3.6.1. WriterT with IO

Try adapting the firewall simulator of example 17 to include a timestamp on each log entry (dont worry about merging entries). The necessary changes should look something like this:

Code available in example22.hs

-- this is the format of our log entries

data Entry = Log {timestamp::ClockTime, msg::String} deriving Eq

instance Show Entry where

show (Log t s) = (show t) ++

-- this is the combined monad type type LogWriter a = WriterT [Entry] IO a

-- add a message to the log logMsg :: String -> LogWriter () logMsg s = do t <- liftIO getClockTime tell [Log t s]

-- this handles one packet

filterOne :: [Rule] -> Packet -> LogWriter (Maybe Packet) filterOne rules packet = do rule <- return (match rules packet)

case rule of

Nothing -> do logMsg ("DROPPING UNMATCHED

PACKET: " ++ (show packet))

return Nothing (Just r) -> do when (logIt r) (logMsg ("MATCH: " ++ (show r) ++ " <=> " ++ (show packet)))

case r of

(Rule Accept ) -> return

(Just packet)

(Rule Reject ) -> return

Nothing

-- this filters a list of packets, producing a filtered packet list -- and a log of the activity

filterAll :: [Rule] -> [Packet] -> LogWriter [Packet] filterAll rules packets = do logMsg "STARTING PACKET FILTER"

out <- mapM (filterOne rules) packets logMsg "STOPPING PACKET FILTER" return (catMaybes out)

ФП 02005-03 01

Лист 62

№ докум.

Копиоова Фоомат



[стр.Начало] [стр.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]