Haskell 组合状态和状态转换器动作

Haskell 组合状态和状态转换器动作,haskell,monad-transformers,Haskell,Monad Transformers,我有几个Statemonad操作。一些操作根据当前状态和其他可选的生成结果的输入做出决策。这两种类型的操作相互调用 我用State和StateT-Maybe对这两种动作类型进行了建模。以下(人为的)示例显示了我当前的方法 {-# LANGUAGE MultiWayIf #-} import Control.Monad (guard) import Control.Monad.Identity (runIdentity) import Control.Monad.Trans.State typ

我有几个
State
monad操作。一些操作根据当前状态和其他可选的生成结果的输入做出决策。这两种类型的操作相互调用

我用
State
StateT-Maybe
对这两种动作类型进行了建模。以下(人为的)示例显示了我当前的方法

{-# LANGUAGE MultiWayIf #-}

import Control.Monad (guard)
import Control.Monad.Identity (runIdentity)
import Control.Monad.Trans.State

type Producer      = Int -> State  [Int] Int
type MaybeProducer = Int -> StateT [Int] Maybe Int

produce :: Producer
produce n
    | n <= 0    = return 0

    | otherwise = do accum <- get
                     let mRes = runStateT (maybeProduce n) accum

                     if | Just res <- mRes -> StateT $ const (return res)
                        | otherwise        -> do res <- produce (n - 1)
                                                 return $ res + n

maybeProduce :: MaybeProducer
maybeProduce n = do guard $ odd n
                    modify (n:)

                    mapStateT (return . runIdentity) $
                        do res <- produce (n - 1)
                           return $ res + n
{-#语言多路If}
进口管制.单子(警卫)
导入控制.Monad.Identity(runIdentity)
进口管制.单子.跨州
类型Producer=Int->State[Int]Int
键入MaybeProducer=Int->StateT[Int]Maybe Int
生产者
产生
|n
我也不想将
状态
操作提升到
状态,因为它可能会造成不准确的模型

你说的“推广”是什么意思?我不知道你指的是哪一个:

  • 重写
    状态
    操作的定义,使其类型现在为
    StateT Maybe
    ,即使它们根本不依赖
    Maybe
  • 使用适配器函数将
    状态s a
    转换为
    状态s可能是a
  • 我同意拒绝(1),但对我而言,这意味着:

    • 选择(2)。一个有用的工具是使用()
    • 重写来自
      状态sa
      的操作,以使用
      单子m=>StateT s m a
    在第二种情况下,类型与任何Monad
    m
    兼容,但不允许代码采用任何特定的基Monad,因此可以获得与
    状态sa
    相同的纯度

    我想试一试。请注意:

    • State s a
      =
      StateT s Identity a
    • 提升机概括::(MFunctor t,Monad m)=>t标识a->t m a
    • 它专门用于提升泛化::State sa->StateT s可能是一个

    编辑:毫无意义的是,所有m的
    状态sa
    之间存在同构。StateT s m a
    类型,由以下反函数给出:

    {-# LANGUAGE RankNTypes #-}
    
    import Control.Monad.Morph
    import Control.Monad.Trans
    import Control.Monad.Trans.State
    import Control.Monad.Identity
    
    fwd :: (MFunctor t, Monad m) => t Identity a -> t m a
    fwd = hoist generalize
    
    -- The `forall` in the signature forbids callers from demanding any
    -- specific choice of type for `m`, which allows *us* to choose 
    -- `Identity` for `m` here.
    bck :: MFunctor t => (forall m. t m a) -> t Identity a
    bck = hoist generalize
    

    因此,
    Monad m=>StateT s m a
    mmorph
    解决方案实际上是相同的。不过,我更喜欢在这里使用
    mmorph

    mapStateT
    很好,我只想使用
    而已。runIdentity
    作为它的论据。大致相同的问题(虽然理论性更强):是的,我指的是(1)。Monad m=>StateT s m a
    是一个巧妙的技巧,尽管不是很明显,从API的角度来看,我觉得有点误导。当然,这完全有道理——“我不依赖于内在单子的类型”
    mmorph
    看起来是这项工作的合适工具,谢谢。“Monad m=>StateT s m a是一个巧妙的技巧,虽然不是很明显,但从API的角度来看,我觉得有点误导。”我同意它不太清楚,但严格来说,它不会误导人,因为类型将同构于
    State s a
    。请参阅我对答案的编辑。