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
应用到 a
和 b
的内部,我仍然会得到 c
类型的东西,我必须将其推入 Gen
,我不知道如果没有 mkGen
或者不显式使用 (,)
(就像我在上面的实现中所做的那样)我怎么能做到这一点。
即使假设文本暗示我应该使用 mkGen
,我应该如何着手摆脱状态线程?
经过一些编辑,我想出了这个
generalB2' f a b = genTwo a (genTwo b . (mkGen .) . f)
但我几乎不相信这是预期的解决方案,因为在我看来它远非可读性。此外,在挑战中,获得这种形式比其他任何形式都难,但它毕竟只是机械的,所以从理解 monad 的角度来看,它并没有真正构成困难,我相信,所以我我真的认为我在这里走错了路,我需要一些帮助。
我想知道挑战的作者是否在 Whosebug 上闲逛。
您的解决方案可能接近预期的解决方案,尽管您可以通过 eta 扩展使其更具可读性。您甚至可以考虑使用 do
表示法来编写它,但仍然使用 genTwo
和 mkGen
.
据我所知,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
语法)。
我正在经历 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 ofgenTwo
. Go back and look at yourgeneralB
implementation and if you didn’t write it in terms ofgenTwo
, do that now and call itgeneralB2
. Doing this should get rid of the state threading between generators.
我不清楚解决这个问题的方法是什么,特别是考虑到上面的段落没有提到 mkGen
。假设我能够将 f
应用到 a
和 b
的内部,我仍然会得到 c
类型的东西,我必须将其推入 Gen
,我不知道如果没有 mkGen
或者不显式使用 (,)
(就像我在上面的实现中所做的那样)我怎么能做到这一点。
即使假设文本暗示我应该使用 mkGen
,我应该如何着手摆脱状态线程?
经过一些编辑,我想出了这个
generalB2' f a b = genTwo a (genTwo b . (mkGen .) . f)
但我几乎不相信这是预期的解决方案,因为在我看来它远非可读性。此外,在挑战中,获得这种形式比其他任何形式都难,但它毕竟只是机械的,所以从理解 monad 的角度来看,它并没有真正构成困难,我相信,所以我我真的认为我在这里走错了路,我需要一些帮助。
我想知道挑战的作者是否在 Whosebug 上闲逛。
您的解决方案可能接近预期的解决方案,尽管您可以通过 eta 扩展使其更具可读性。您甚至可以考虑使用 do
表示法来编写它,但仍然使用 genTwo
和 mkGen
.
据我所知,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
语法)。