状态Monad在Haskell中包含Random和List

时间:2016-11-08 21:03:36

标签: haskell monads state-monad

当我正在学习我的考试功能编程时,我仍然试图真正理解 Monads。还有什么比自己定义一个更好的方法?我定义了这个:

newtype ST a = ST (State -> ([a], State))
type State = StdGen

基本上是List Monad和Random Monad合二为一。 这个monad应该可以让你使用随机函数和列表。 现在遇到了麻烦,因为我能够定义return函数,但>>=并没有完全解决问题。

instance Monad ST where
    return a = ST $ \g -> ([a], g)
    -- M a -> (a -> M b) -> M b
    st >>= f  = ST $ \s -> 
                let (x,s') = st s 
                in (f x) s'

代码的灵感来自This paper p.218

有任何解释吗?

2 个答案:

答案 0 :(得分:5)

让我们小心跟踪所有类型(当我编写棘手的代码时,我自己这样做)。首先,让我们为您的ST类型添加一个访问器,这将使事情变得更容易。

newtype ST a = ST { runST :: State -> ([a], State) }

现在我们有runST :: ST a -> State -> ([a], State)。在定义monad代码时,我希望立即将runST应用于所有ST值,因此我知道我正在使用的是哪种类型。

st >>= f = ST $ \s ->
    -- runST st  :: State -> ([a], State)
    -- f         :: a -> ST b
    -- runST . f :: a -> State -> ([b], State)
    -- s         :: State
    let (as, s') = runST st s in  -- I renamed x to as for readability
    -- as        :: [a]
    -- s'        :: State

现在我们需要一个([b], State)。我们可以使用b获取f。我们有一个a的列表,所以让我们尝试映射

    -- map (runST . f) as :: [State -> ([b], State)]

嗯,这不太有用,让我们尝试应用我们进来的状态:

    -- map (\a -> runST (f a) s) as :: [([b], State)]

也许我们可以解决这个问题。我们列出了b以及其他一些内容。我们将此rs命名为("结果"):

    let rs = map (\a -> runST (f a) s) as in

现在我们可以通过连接所有结果b来获取b的列表:

    let bs = concat (map fst rs) in  -- bs :: [b]

所以这可能是我们想要回归的。现在哪个State我们想要用它?我们遇到了问题,因为我们有很多不同的State可供选择。我们选择列表中的最后一个,还是第一个?如果列表是空的,我们只需返回进来的State。这些是任意选择 - 正如我的物理学教授曾经说过的那样:"现在我们必须做出选择,这是一个问题,因为我们要犯错误的一个"。这在函数式编程中非常正确;无论何时你必须做出像这样的任意选择,你可能会搞砸了。

如果我们退后一步并直观地思考其含义,ST a计算将采用一种状态并返回一个选项列表,其中包含一个新状态以供将来计算,每个 将生成一个选择列表和一个新状态。我们可以使用concat将所有列表组合在一起,但我们没有办法将所有状态组合成一个。使用随机API我们没有这个(我们可以想象可能bitxor在一起所有的状态......)。

如果没有一种结合状态的方法,我们的monad bind将不得不忘记它拥有的大部分数据,这是一个不好的标志,它不会遵守法律(尽管有一个monad这个复杂的我担心证明法律的复杂性)。据我所知,这种类型没有monad。

一个类型的monad:

newtype ST' a = ST' { runST' :: State -> [(a, State)] }

它相当于StateT State []。现在计算的每个分支都有自己的随机状态,因此我们不需要将许多状态组合成一个状态。你可能有更好的运气来定义这个monad - 像我一样做,写下你所知道的所有类型,并尝试以类型导向的方式获得你需要的东西。尽量不要忘记"任何信息,并努力在构造输出时使用每个输入一次。

很抱歉,这篇文章有点模糊和粗暴 - 我意识到在定义monad时我使用了很多直观的原则,我想我会尝试分享它们。请记住,它不足以获得类型检查的定义(尽管这确实可以让你获得很多方法):检查法律,否则当你去使用时会发生奇怪的事情{{1符号等。

答案 1 :(得分:2)

关注luqui's lead,我们得到了

st >>= f = ST (g . runST st)
    -- runST st  :: State -> ([a], State)
    -- f         :: a -> ST b
    -- runST . f :: a -> State -> ([b], State)

    where
        g (a:as,s) = let  (bs, s2) = (runST . f) a s 
                          (rs, sn) = g (as, s2)
                     in   (bs ++ rs, sn) 
        g ([], s)  = ([], s) 

(未经测试)。