【问题标题】:help on writing "the colist Monad" (Exercise from an Idioms intro paper)帮助编写“colist Monad”(习语介绍论文中的练习)
【发布时间】:2011-09-21 17:26:44
【问题描述】:

我正在阅读 Conor McBride 和 Ross Paterson 的“Functional Pearl / Idioms: applicative programming with effects:”(new 版本,标题中带有“idioms”)。我在练习 4 中遇到了一点困难,下面将对此进行解释。任何提示将不胜感激(特别是:我应该开始写fmapjoin 还是return>>=?)。

问题陈述

你想在哪里创建一个instance Monad []

return x = repeat x

ap = zapp

标准库函数

如第 p.论文的 2,ap 将一元函数值应用于一元值。

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 应用程序”)将一个列表中的函数应用于另一个列表中的对应值,即,

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

我的困难

请注意,在扩展形式中,mf :: m (a -&gt; b) 在我们的例子中是函数[(a -&gt; b)] 的列表。所以,在&gt;&gt;=的第一个应用中,我们有

(f:fs) >>= mu

在哪里mu = (\f -&gt; (ms &gt;&gt;= \s -&gt; return (f s)))。现在,我们可以将fs &gt;&gt;= mu 作为子程序调用,但这并不知道要删除ms 的第一个元素。 (回想一下,我们希望结果列表是 [f1 s1, f2 s2, ...]。我试图破解一些东西,但是......正如预期的那样,它没有用......任何帮助将不胜感激。

提前致谢!

编辑 1

我想我已经成功了;首先,我按照用户“comonad”的建议,用 fmapjoin 重写了 ap

我的信念飞跃假设fmap = map。如果有人能解释如何到达那里,我将非常感激。在此之后,很明显join 在用户“comonad”建议的列表列表中起作用,并且应该是对角线\x -&gt; 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 页上的“解决方案”)...谢谢大家的帮助!

【问题讨论】:

  • 关于fmapApplicative 法律包括身份pure f &lt;*&gt; x = f &lt;$&gt; x。您应该能够使用它将fmap 显示为map。是的,join 采用嵌套列表的对角线——作为后续练习,您可以尝试展示对角线在不同长度的列表上定义不明确如何违反单子定律。

标签: haskell monads idioms


【解决方案1】:

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

必然无限的列表(即streams),通过排除不规则的情况,确实形成了一个monad,其ap 的行为类似于zapp。作为线索,请注意 Stream x 与 Nat -> x 同构。

对于造成的混乱,我深表歉意。将旧的、未完成的草稿(充满错误)留在网上(哈哈)有时很危险。

【讨论】:

  • 谢谢!不过,我确实喜欢旧版本的练习:)
  • 哦!这现在更有意义了。我觉得很奇怪,这个练习没有表明建议的 monad 只在等长的列表上有效。
  • @camccann 这提醒了我:所有固定长度的向量类型构造函数都是一元的,通过类似的构造(包括我最喜欢的长度为零的向量)。
  • @Conor McBride:嗯,任何固定形状的东西都可以按元素组合,所有这些都是同一个粗糙的单子家族,通常比Applicative 更有意义。例如,您可以“压缩”树而不是替换叶子。将a -&gt; b 解释为bs 的结构,其形状由a 给出,reader monad 也是其中之一。肯定会有某种简洁的抽象概括整个事情......
【解决方案2】:

Monad 的最小完整定义是fmap+return+joinreturn+(&gt;&gt;=)。您可以将一个与另一个一起实现:

(>>=) :: 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

在练习中,给出了fmapreturnap的语义。 其余的一目了然,只要您检查一个示例即可:

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)
                              ...
                              ]

【讨论】:

  • 只是好奇,flip fmap 绝对有意义吗? (如果将fmap 解释为某种函子是否公平?)
  • @gatoatigrado:这是有道理的,因为 Haskell 类型类别上的 endofunctor 等效于该类别中的态射族。在分类术语中,join :: m (m a) -&gt; m a 是一种自然变换,fmap :: (a -&gt; b) -&gt; (f a -&gt; f b) 是“不同”类别中的箭头之间的函子映射……但 Haskell 将所有类似态射的东西扁平化为 (-&gt;)
【解决方案3】:

嗯。我不禁认为这个练习有点不公平。

练习 4(colist Monad)

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

评论这个 monad 的 ap 和我们的 zapp 的相对效率。

首先,我相当确定所讨论的 monad 实例一般对 [] 无效。当他们说“共归纳解释”时,我怀疑这是指无限列表。在某些情况下,该实例实际上对有限列表有效,但通常不适用于任意列表。

这是您的第一个非常笼统的提示——为什么 monad 实例只对某些列表有效,尤其是无限列表?

这是您的第二个提示:考虑到本文前面的其他定义,fmapreturn 是微不足道的。你已经有returnfmap 只是稍微不那么明显。

此外,(&gt;&gt;=) 在其他功能方面也很容易实现,就像任何Monad 一样,这使得join 成为问题的症结所在。在大多数情况下,(&gt;&gt;=) 更适合编程,但join 在概念上更基础,在这种情况下,我认为更易于分析。所以我建议继续努力,暂时忘记(&gt;&gt;=)。一旦你有了一个实现,你就可以返回并重建(&gt;&gt;=) 并检查 monad 法则以确保它一切正常。

最后,假设您有fmap 可用,但没有别的。给定类型为[a -&gt; b][a] 的值,您可以将它们组合起来得到[[b]] 类型的东西。这里join的类型是[[a]] -&gt; [a]。您如何编写 join 以便在此处获得与在原始值上使用 zapp 相同的结果?请注意,关于相对效率的问题和问题一样,是关于实现的线索。

【讨论】:

  • @camccann:有一点我不清楚。如果“共归纳解释”指的是无限列表(我同意这很可能),那么 repeatzapp 实际上不会形成一个不适合所说的共归纳解释的单子吗? ZipList 就是这种情况,它没有有效的 monad 实例。无论如何,我怀疑作者选择这个例子是为了说明应用函子有用的部分原因是它们比 monad 更通用(真遗憾,我还没有读过论文)。
  • @John L: Errrh...你是对的,这相当于ZipList,但你错过了其他一些事情,如果不破坏答案,我无法真正澄清gatoatigrado 正在进行的练习。 :[
  • @camccan:我害怕那个。我只是阅读论文,希望能详细说明。
  • @John L:可能不是——我只是略读了一下(之前读过旧版本),但我认为这个特定的 monad 不会出现在我完整引用的练习之外。也就是说,这是一本好书,所以试一试也无妨!但是,如果您希望获得更直接的解释而不是自己完成练习,请随时通过电子邮件与我联系。
  • @camccann:你说的很对,这篇论文对这个目的毫无用处(尽管其他方面很好读!)。但是我确实注意到我误读了这个练习并且正在考虑一个常规的列表单子,而不是 colist 单子。这很好地清除了它。
猜你喜欢
  • 2014-08-07
  • 1970-01-01
  • 1970-01-01
  • 2015-06-11
  • 1970-01-01
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多