Monad 挑战——一个被遗漏的概括

The Monad Challenges - A Missed Generalization

我正在经历 The Monad Challenges

A Missed Generalization 部分,我应该至少有这段代码(我删除了与问题无关的部分),其中 Gen 看起来很像 State Monad ,

-- Imports and other stuff that hide Prelude
-- For instance, the do notation is NOT available at this stage of the challenges

type Gen a = Seed -> (a,Seed)

genTwo :: Gen a -> (a -> Gen b) -> Gen b
genTwo g f s = let (a,s') = g s
               in f a s'

mkGen :: a -> Gen a
mkGen a s = (a,s)

generalB :: (a -> b -> c) -> Gen a -> Gen b -> Gen c
-- I've implemented it as follows and it works
generalB f a b s = let (x,s') = a s
                       (y,s'') = b s'
                   in (f x y,s'')

“作业”的文本为

[…] you might not have implemented generalB in terms of genTwo. Go back and look at your generalB implementation and if you didn’t write it in terms of genTwo, do that now and call it generalB2. Doing this should get rid of the state threading between generators.

我不清楚解决这个问题的方法是什么,特别是考虑到上面的段落没有提到 mkGen。假设我能够将 f 应用到 ab 的内部,我仍然会得到 c 类型的东西,我必须将其推入 Gen,我不知道如果没有 mkGen 或者不显式使用 (,)(就像我在上面的实现中所做的那样)我怎么能做到这一点。

即使假设文本暗示我应该使用 mkGen,我应该如何着手摆脱状态线程

经过一些编辑,我想出了这个

generalB2' f a b = genTwo a (genTwo b . (mkGen .) . f)

但我几乎不相信这是预期的解决方案,因为在我看来它远非可读性。此外,在挑战中,获得这种形式比其他任何形式都难,但它毕竟只是机械的,所以从理解 monad 的角度来看,它并没有真正构成困难,我相信,所以我我真的认为我在这里走错了路,我需要一些帮助。

我想知道挑战的作者是否在 Whosebug 上闲逛。

您的解决方案可能接近预期的解决方案,尽管您可以通过 eta 扩展使其更具可读性。您甚至可以考虑使用 do 表示法来编写它,但仍然使用 genTwomkGen.

据我所知,mkGen 是一个 'disguised' return 函数,而 genTwo 同样是一个 'disguised' 单子绑定(即 >>=).

generalB(和generalB2)的类型等同于liftM2,即is implemented like this:

liftM2  :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 f m1 m2 = do { x1 <- m1; x2 <- m2; return (f x1 x2) }

也就是说,根据 return>>=(您看不到,因为它使用 do 语法)。