帮助撰写“colist monad”(从成语介绍练习)

时间:2011-06-24 03:34:37

标签: haskell monads idioms

我正在阅读Conor McBride和Ross Paterson的“功能性珍珠/成语:带效果的应用编程:”( new 版本,标题中带有“成语”)。我对练习4有点困难,这将在下面解释。任何提示都会非常感激(特别是:我应该开始撰写fmapjoinreturn>>=吗?)。

问题陈述

您想要创建一个instance Monad []

return x = repeat x

ap = zapp

标准库函数

如同p。在论文的第2篇中,ap将monadic函数值应用于monadic值。

ap :: Monad m => m (s -> t) -> m s -> m t
ap mf ms = do
    f <- mf
    s <- ms
    return (f s)

我将规范符号扩展为

ap mf ms = mf >>= (\f -> (ms >>= \s -> return (f s)))

特定于列表的函数zapp(“zippy application”)将一个列表中的函数应用于另一个列表中的对应值,即

zapp (f:fs) (s:ss) = f s : zapp fs ss

我的困难

请注意,在展开的表单中,mf :: m (a -> b)是我们案例中的函数列表[(a -> b)]。因此,在>>=的第一个应用中,我们有

(f:fs) >>= mu

其中mu = (\f -> (ms >>= \s -> return (f s)))。现在,我们可以将fs >>= mu作为子例程调用,但是这不知道要删除ms的第一个元素。 (回想一下,我们希望得到的结果列表是[f1 s1,f2 s2,...]。我试图破解一些东西但是...正如预测的那样,它没有用......任何帮助都会非常感激。< / p>

提前致谢!

编辑1

我想我得到了它的工作;首先,我使用apfmap重写join作为用户“comonad”建议。

我的信仰的飞跃是假设fmap = map。如果有人能解释如何到达那里,我会非常感激。在此之后,显然join适用于用户“comonad”建议的列表列表,并且应该是对角线\x -> zipWith ((!!) . unL) x [0..]。我的完整代码是:

newtype L a = L [a] deriving (Eq, Show, Ord)
unL (L lst) = lst
liftL :: ([a] -> [b]) -> L a -> L b
liftL f = L . f . unL

joinL :: L (L a) -> L a
joinL = liftL $ \x -> zipWith ((!!) . unL) x [0..]

instance Functor L where
    fmap f = liftL (map f)

instance Monad L where
    return x = L $ repeat x
    m >>= g = joinL (fmap g m)

希望这是正确的(似乎是本文第18页的“解决方案”)...感谢大家的帮助!

3 个答案:

答案 0 :(得分:11)

嗯。我不禁想到这次演习有点不公平。

  

练习4(colist Monad)

     

虽然repeatzapp不是通常return实例的apMonad [],   它们是替代monad的returnap,更适合   对[]的共同解释。这个monad的join :: [[x]] → [x]是什么?

     

评论此monad的apzapp的相对效率。

首先,我非常确定有问题的monad实例[]一般无效。当他们说“coinductive explain”时,我怀疑这是指无限列表。在某些情况下,实例实际上对有限列表有效,但一般不适用于任意列表。

这是你的第一个,非常一般的提示 - 为什么monad实例只对某些列表有效,特别是无限的?

这是您的第二个提示:fmapreturn在本文前面的其他定义中是微不足道的。你已经有return; fmap只是略显不明显。

此外,(>>=)在其他功能方面也很容易实现,就像任何Monad一样,join是问题的关键。在大多数情况下(>>=)对于编程来说更自然,但join在概念上更具基础性,在这种情况下,我认为,分析更直接。所以我建议你继续努力,暂时忘掉(>>=)。实施后,您可以返回并重新构建(>>=)并检查monad法则以确保一切正常。

最后,假设您有fmap可用,但没有别的。给定类型为[a -> b][a]的值,您可以将它们组合起来以获得[[b]]类型的值。此处join的类型为[[a]] -> [a]。您如何撰写join,以便在此处获得与使用zapp原始值相同的结果?请注意,关于相对效率的问题以及问题是关于实施的线索。

答案 1 :(得分:7)

我只是想我应该澄清一下,标题中带有练习和“成语”的版本是该论文的早期早期草案,最终出现在JFP中。当时,我错误地认为colists(我的意思是可能是无限的,可能是有限的列表)是一个monad,其方式与zapp相对应:有一个似乎合理的候选者(在其他答案中提到)但是Jeremy Gibbons很友好地向我们指出它不符合monad法则。反例涉及具有不同有限长度的列表的“参差不齐”列表。相应地,在JFP文章中,我们得到了纠正。 (我们对它感到非常高兴,因为我们喜欢找到那些(&lt; *&gt;)不是Monad的ap的应用函子。)

必然无限的列表(即 streams ),通过排除粗糙的案例,确实形成了一个其行为像zapp的monad。有关线索,请注意Stream x与Nat同构 - > X

我为这种困惑道歉。在网络上留下旧的,未完成的草稿(充满错误)有时很危险。

答案 2 :(得分:3)

Monad的最小完整定义是fmap + return + joinreturn + (>>=)。你可以用另一个来实现:

(>>=) :: Monad m => m a -> (a->m b) -> m b
(>>=) ma amb = join $ fmap amb ma

fmap :: Monad m => (a->b) -> m a -> m b
fmap f ma = ma >>= (return . f)
join :: Monad m => m (m a) -> m a
join mma = mma >>= id

现在,ap的实施可以用joinfmap重写:

ap :: Monad m => m (a->b) -> m a -> m b
ap mf ma = do
    f <- mf
    a <- ma
    return (f a)
ap mf ma = do
    f <- mf
    fmap f ma
ap mf ma = join $ fmap (flip fmap ma) mf

在练习中,给出了fmapreturn以及ap的语义。 一旦你看了一个例子,剩下的就很明显了:

ap [f1,f2,f3...] [1,2,3...] = join $ fmap (flip fmap [1,2,3...]) [f1,f2,f3...]
                            = join $ [ [(f1 1), f1 2 , f1 3 ...]
                                     , [ f2 1 ,(f2 2), f2 3 ...]
                                     , [ f3 1 , f3 2 ,(f3 3)...]
                                     ...
                                     ]
                            = [(f1 1)
                              ,     (f2 2)
                              ,          (f3 3)
                              ...
                              ]