Are*_* Fu 2 monads state haskell writer reader
我围绕使用RWS(Reader+ Writer+ State)monad 构建计算:
newtype Problem a = Problem { unProblem :: RWS MyEnv MyLog MyState a }
deriving ({- lots of typeclasses -})
Run Code Online (Sandbox Code Playgroud)
通过组装表单的基本计算逐步构建计算
foo :: a -> Problem b
Run Code Online (Sandbox Code Playgroud)
然而,有时候,子计算不需要RWSmonad 的全部功能.例如,考虑一下
bar :: c -> State MyState d
Run Code Online (Sandbox Code Playgroud)
我想bar在Problemmonad 的上下文中使用作为更大计算的一部分.我可以看到三种方式,这对我来说似乎都不是很优雅.
手动解包State计算并将其重新包装在RWS monad中:
baz :: a -> RWS MyEnv MyLog MyState c
baz x = do temp <- foo x
initialState <- get
let (finalResult, finalState) = runState (bar temp) initialState
put finalState
return finalResult
Run Code Online (Sandbox Code Playgroud)bar通过将其提升到Problemmonad 中来修改类型签名.这有一个缺点,即新类型签名没有明确承诺bar独立于MyEnv并且不记录任何内容MyLog.
用RWS显式ReaderT MyEnv WriterT MyLog State MyStatemonad堆栈替换monad.这使我能够简洁地lift.lift将子bar计算转换为完整的monad; 但是,这个技巧不适用于例如表格的子计算c -> Reader MyEnv d.
有没有组成一个更清洁的方式foo和bar?我有一种预感,一些聪明的类型类实例定义可能会成功,但我无法确切地知道如何继续.
我假设您正在使用mtl(如果您不这样做,请考虑这样做 - 这些库大部分是兼容的,除了后面的内容).你可以得到的情况下MonadReader MyEnv,MonadWriter MyLog和MonadState MyState.然后,您可以使用这些来通过具有这些约束的任何 monad堆栈来概括您的函数.
{-# LANGUAGE GeneralizedNewtypeDeriving, MultiParamTypeClasses, FlexibleContexts #-}
import Control.Monad.RWS
import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.State
newtype Problem a = Problem { unProblem :: RWS MyEnv MyLog MyState a }
deriving (Functor, Applicative, Monad,
MonadReader MyEnv, MonadWriter MyLog, MonadState MyState)
Run Code Online (Sandbox Code Playgroud)
从你的例子中,可能bar只需要知道有一些状态MyState,所以你可以给它签名
bar :: MonadState MyState m => c -> m d
bar = ...
Run Code Online (Sandbox Code Playgroud)
然后,即使对于foo可能需要完整RWS功能的内容,您也可以编写
foo :: (MonadState MyState m, MonadReader MyEnv m, MonadWriter MyLog m) => a -> m b
foo = ...
Run Code Online (Sandbox Code Playgroud)
然后,您可以根据自己的喜好混合搭配:
baz :: Problem ()
baz = foo 2 >> bar "hi" >> return ()
Run Code Online (Sandbox Code Playgroud)
现在,为什么这一般有用?它归结为你的monad"堆栈"的灵活性.如果明天你决定,你实际上并不需要RWS,但只有State你可以重构Problem相应的和bar(只曾经需要摆在首位状态)将继续无需任何修改工作.
一般情况下,我尽量避免使用WriterT,StateT,RWST等里面像辅助功能foo或bar.选择在那里让你的代码,一般和实现无关尽可能使用类型类MonadWriter,MonadState,MonadReader等.然后,你只需要使用WriterT,StateT,RWST一旦你的代码中:当你真正"跑"的单子.
transformers如果您正在使用transformers,这一切都无效.这不一定是坏事:mtl由于总是能够"找到"组件(如州或作家)有一些问题.
| 归档时间: |
|
| 查看次数: |
171 次 |
| 最近记录: |