管理,定相,组合monad的资源(在Scala或Haskell中)

时间:2014-03-04 21:59:05

标签: scala haskell monads

我正在寻找讨论组成monad的良好实践的资源。我最紧迫的问题是我正在编写一个系统,该系统正在使用一系列状态monad而不是不同的状态类型,似乎处理这种情况的最好方法就是创建一个大的产品类型(可能是prettied)即使第1阶段对组件B不感兴趣,第2阶段仅对组件A.1感兴趣,也包括我感兴趣的所有组件。

在这类领域编写代码时,我非常感谢能够很好地讨论替代方案。我自己的代码库是在Scala中,但我很高兴阅读有关Haskell中相同问题的讨论。

1 个答案:

答案 0 :(得分:8)

使用StateT的堆栈有点困难,因为当您撰写getput时,您正在谈论哪一层会让您感到困惑。如果你使用transformers样式的显式堆栈,则必须使用一堆lift s,如果使用mtl的基于类的方法,则完全卡住了。

-- using transformers explicit stack style
type Z a = StateT Int (StateT String IO) a

go :: Z ()
go = do int <- get
        str <- lift get
        replicateM int (liftIO $ putStrLn str)

我们可能希望避免使用明确的产品类型的状态。由于我们最终得到了从产品状态到每个单独组件的功能,因此使用get

可以轻松gets进入这些单个组件
data ZState = ZState { int :: Int, str :: String }
type Z a = StateT ZState IO a

go :: Z ()
go = do i <- gets int
        s <- gets str
        replicateM i (liftIO $ putStrLn s)

但这可能会被认为是丑陋的,原因有两个:(1)put一般情况下修改并没有任何好的故事和(2)我们不容易看到在影响的函数类型,例如int状态,并知道它不会触及str。我们更喜欢保持这种类型保证的模块化。

如果您lens - 精明,那就是一个名为zoom

的解决方案
-- the real type is MUCH more general
zoom :: Lens' mother child -> StateT child m a -> StateT mother m a 

&#34;提升&#34;对一个较大的状态空间的子部分进行有状态计算直到整个状态空间。或者,实际上,我们这样使用它:

data ZState = ZState { _int :: Int, _str :: String }
makeLenses ''ZState

type Z = StateT ZState IO a

inc :: MonadState Int m => m ()
inc = modify (+1)

yell :: MonadState String m => m ()
yell = modify (map toUpper)

go :: Z ()
go = do zoom int $ do inc
                      inc
                      inc
        zoom str yell
        i <- use int
        s <- use str
        replicateM i (liftIO $ putStrLn s)

现在大多数问题都应该消失了 - 我们可以zoom来隔离只依赖于incyell等总状态子集的有状态操作并确定它们隔离在他们的类型。我们还可以get使用use zoom内部状态组件。

除此之外,zoom可用于type Z a = EitherT String (ListT (StateT ZState IO)) a >>> :t zoom int :: EitherT String (ListT (StateT Int IO)) a -> Z a zoom int :: EitherT String (ListT (StateT Int IO)) a -> Z a 处于深埋在各种变压器堆栈内的状态。完全通用的类型在这种情况下工作得很好

zoom

虽然这非常好,但完全通用{{1}}需要一些重要的诡计,你只能缩放一些变形金刚层。 (今天我很不清楚你如何将这个功能添加到你自己的图层,尽管可能是这样的。)