我的意思是为什么不到最后?
由于这个评估变换器堆栈的惯例,人们必须编写一个类似的尴尬之处:
runStateT (runReaderT (runRWST stack r s) r') s'
而不是:
runStateT s' $ runReaderT r' $ runRWST r s $ stack
将它与立即do
结合起来变得更加尴尬:
let
action = do
liftIO $ putStrLn "Do"
liftIO $ putStrLn "something"
in runStateT (runReaderT (runRWST action r s) r') s'
而不是:
runStateT s' $ runReaderT r' $ runRWST r s $ do
liftIO $ putStrLn "Do"
liftIO $ putStrLn "something"
这是否有任何动机,或者这只是一个不幸的约定?
BTW,我确实意识到当前的约定使得使用记录语法实现“运行”功能变得容易,但这不能成为一个论据,因为库必须更易于实现的易用性。答案 0 :(得分:4)
在Parameter order的HaskellWiki条目和this Stack Overflow问题中,有两条参数顺序建议:
根据我使用Reader / State monad的经验,在不同的环境/状态下调用相同的计算比在相同的环境/状态下调用不同的 monadic计算更频繁。当前的参数顺序很方便。
另一个原因:读取器/状态monadic值的行为非常类似于将环境/初始状态作为参数的函数。在Haskell函数中,在参数之前进行。
为避免嵌套ReaderT
/ StateT
/ RWST
变换器的必要性,您可以使用一个承载全局状态/环境的RWST
转换器,并使用{来自lens库的{1}}和zoom
来调整在更受限制的环境中工作的计算。
答案 1 :(得分:3)
我确信这是一种权宜之计。这很容易定义
newtype StateT = StateT { runStateT :: s -> m (a, s) }
并完成它。
答案 2 :(得分:0)
我想添加一致性作为混合的答案。这是所有的推测,所以带上一粒盐:
初始实现使用newtype记录语法来编写这些数据定义,因此数据在运行时首先出现,不知道这是否比作为最后一个参数的数据更可取。较新的数据类型只是遵循这一惯例。