15

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

(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)
(=<<) :: (a -> r -> b) -> (r -> a) -> (r -> b)

因此,就像(<*>)and (>>=)/(=<<)采用二元函数和一元函数,并通过后者约束前者的两个参数之一与另一个参数确定。毕竟,我们知道对于函数 applicative/monad,

f <*> g = \x -> f x (g x)
f =<< g = \x -> f (g x) x 

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

至于 monad 比 applicative functors “更强大”,在LYAH 的For a Few Monads More章节的硬拷贝中,陈述如下:

[…]join不能仅通过使用仿函数和应用程序提供的功能来实现。

join不能用(<*>),pure和来实现fmap

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

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

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

但是,考虑到列表 applicative/monad,我可以join在不显式使用(=<<)/(>>=)return(甚至不是(<*>),fwiw)的情况下表达join = concat:所以可能实现join f = flip ($) <*> f也是一种技巧,并没有真正表明我是依赖Applicative还是依赖Monad.

4

2 回答 2

21

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

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

于 2021-05-31T22:30:30.230 回答
2

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

据我所知,明确的问题是:分别使用 Monad / Applicative / Functor 中的方法作为原语,根本不使用显式模式匹配,是可以严格表示为更大的计算类或正在使用的另一组原语。现在可以有意义地回答这个问题。

但是函数是不透明的。无论如何都不存在模式匹配。在不限制我们可以使用的情况下,这个问题再次没有意义。然后限制就变成了,显式使用命名参数,编程的重点风格,所以我们只允许自己以组合风格编码。

那么,对于列表,只有fmapapp( <*>),我们可以表达很多计算,并且添加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 in(->) r (a->b)一样的;功能 特殊

(顺便说一句,我目前还没有看到如何在没有实际编码的情况下app 显式地对列表进行编码join。使用列表推导等同于使用concat; 并且concat不是实现,它)。join join


join f = f <*> id

很简单,所以毫无疑问。


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

(=<<) = (<*>) . flip对于功能。而已。这就是函数 Monad 和 Applicative Functor 相同的意思。flip是一个普遍适用的组合子。concat不是。那里有一定的混淆,当然,功能。但是那里没有特定的函数操作函数(例如concat特定的列表操作函数)或任何地方,因为函数是不透明的。

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

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

(=<<) = (<*>) . flip中,两者flip它们应用的函数(.)都不做任何事情。所以他们不知道这些函数的内部结构。就像,如果该列表是例如,将碰巧正确计算参数列表的长度。说this,建立在this之上,是在使用函数的一些内部知识(与通过模式匹配使用其参数列表的内部知识相同)。但是仅使用诸如等之类的基本组合器并非如此。foo = foldr (\x acc -> x+1) 0[1,2]fooconcatflip(.)

于 2021-05-31T21:27:56.310 回答