函数 monad 真的能提供比函数应用函子更多的东西吗?如果是这样,是什么?

Enr*_*lis 15 monads haskell functional-programming applicative combinatory-logic

对于函数 monad,我发现(<*>)(>>=)/(=<<)有两个非常相似的类型。特别是,(=<<)使相似性更加明显:

(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)
(=<<) :: (a -> r -> b) -> (r -> a) -> (r -> b)
Run Code Online (Sandbox Code Playgroud)

所以它就像(<*>)(>>=)/(=<<)取一个二元函数和一个一元函数,并限制前者的两个参数之一通过后者从另一个参数中确定。毕竟,我们知道对于函数 applicative/monad,

f <*> g = \x -> f x (g x)
f =<< g = \x -> f (g x) x 
Run Code Online (Sandbox Code Playgroud)

它们看起来非常相似(或者对称,如果你愿意的话),我不禁想到标题中的问题。

关于 monad 比应用函子“更强大”,在LYAH 的For a few Monads More章节的硬拷贝中,声明如下:

[...]join不能仅使用函子和应用程序提供的函数来实现。

join不能在以下方面来实现(<*>)purefmap

但是我上面提到的 applicative/mondad函数呢?

我知道,join === (>>= id)对于归结为 的函数 monad \f x -> f x x,即通过将后者的一个参数作为前者的两个参数提供,使二元函数成为一元函数。

我可以用 来表达(<*>)吗?好吧,实际上我认为我可以:对flip ($) <*> f === join f吗?不是flip ($) <*> f的实现join它确实没有(>>=)/(=<<)return

然而,想着列表应用性/单子,我可以表达join不明确使用(=<<)/(>>=)return(甚至没有(<*>),FWIW)join = concat; 所以可能这个实现join f = flip ($) <*> f也是一种技巧,它并没有真正显示出我是仅仅依赖Applicative还是也依赖Monad.

Fyo*_*kin 22

当你这样实现时join,你使用的函数类型知识超出Applicative了你的知识范围。这些知识在使用中编码($)。那就是“应用程序”运算符,它甚至是函数的核心。同样的事情发生在您的列表示例中:您正在使用concat,这是基于对列表性质的了解。

一般而言,如果您可以使用特定 monad 的知识,则可以表达任何幂的计算。例如,Maybe您可以匹配其构造函数并以这种方式表达任何内容。当 LYAH 说 monad 比 applicative 更强大时,它的意思是“作为抽象”,不适用于任何特定的 monad。

  • @Enlico `id` 是一个函数。`(&lt;*&gt; id)` 不是可以与任何应用程序一起使用的多态函数。类似地,只有函数 `(&gt;&gt;= id)` 和 `(id &gt;&gt;=)` 是相同的。 (7认同)

Wil*_*ess 1

edit2:这个问题的问题在于它含糊不清。它使用了一个根本没有定义的概念(“更强大”),让读者猜测其含义。这样我们只能得到毫无意义的答案。当然,在使用我们可以使用的所有 Haskell 库的同时,任何东西都可以编码。这是一个空洞的说法。这不是问题。

据我所知,已解决的问题是:分别使用 Monad / Applicative / Functor 中的方法作为原语,而不使用显式模式匹配,是一类可以因此严格表达为更大的计算或正在使用的其他原语集。现在这个问题可以得到有意义的回答了。

但函数是不透明的。无论如何都不存在模式匹配。如果不限制我们可以使用的内容,这个问题就没有任何意义。那么限制就变成了显式使用命名参数、有意义的编程风格,这样我们就只允许自己以组合风格进行编码。

那么,对于仅使用fmapand app( <*>) 的列表,我们可以表达很多计算,并且添加join到我们的库中确实会使计算量变得更大。函数则不然。join = W = CSI = flip app id。结束。

实现后app f g x = (f x) (g x) = id (f x) (g x) :: (->) r (a->b) -> (->) r a -> (->) r b,我已经有了flip app id :: (->) r (r->b) -> (->) r b,我不妨调用它,join因为类型适合。不管我写与否,它已经存在了。另一方面,从app fs xs :: [] (a->b) -> [] a -> [] b,我似乎无法得到[] ([] b) -> [] b中的两个->s(->) r (a->b)相同;功能比较特殊

(顺便说一句,我目前不知道如何在不实际编码的情况下显式编码列表。使用列表推导式相当于使用; 并且不是实现,它)。app joinconcatconcatjoin join


join f = f <*> id
Run Code Online (Sandbox Code Playgroud)

很简单,所以没有疑问。


编辑:嗯,显然仍然存在疑问)。

(=<<) = (<*>) . flip对于功能。就是这样。这就是说对于函数 Monad 和 Applicative Functor 来说是相同的。flip是一个普遍适用的组合器。concat不是。当然,这与功能存在一定的混淆。但是那里或任何地方都没有特定的函数操作函数(就像concat特定的列表操作函数一样),因为函数是不透明的。

作为一种特定的数据类型,它可以进行模式匹配。作为一个 Monad,虽然它只知道>>=returnconcat确实使用模式匹配来完成其工作。id才不是。

id这里类似于lists' [],而不是concat. 它的工作原理正是意味着被视为 Applicative Functor 或 Monad 的函数是相同的。当然,一般来说 Monad 比 Applicative 更强大,但这不是问题所在。如果你可以用and来表达join列表,我想说这意味着它们对列表也有相同的能力。<*>[]

在 中(=<<) = (<*>) . flip, 和flip它们所应用的函数(.)不执行任何操作。所以他们不了解这些函数的内部结构。就像,如果该列表是例如,将会正确计算参数列表的长度。在此基础上说this正在使用函数的一些内部知识(与通过模式匹配使用其参数列表的内部知识相同)。但仅使用诸如 等基本组合器则不然。foo = foldr (\x acc -> x+1) 0[1,2]fooconcatflip(.)

  • 我不明白。`id` 和 `flip` 都是特定于函数的,而不是 `Applicative` 接口的一部分。(嗯,“id”实际上适用于一般的“Category”,但它仍然不是“Applicative”的一部分。) (2认同)