对于函数monad,我发现(<*>)
和(>>=)
/(=<<)
有两种非常相似的类型。特别是,(=<<)
使相似性更加明显:
(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)
(=<<) :: (a -> r -> b) -> (r -> a) -> (r -> b)
因此,就像(<*>)
和(>>=)
/(=<<)
都采用二进制函数和一元函数,并通过后者将前者的两个参数中的一个限制为从另一个参数确定。毕竟,我们知道对于函数应用/monad,
f <*> g = x -> f x (g x)
f =<< g = x -> f (g x) x
它们看起来非常相似(或者对称,如果你愿意的话),我不禁想到了标题中的问题。
关于monad比应用函子"更强大",在LYAH的">For a Few monads More"一章的硬拷贝中,陈述如下:
[...]
join
不能仅使用函子和应用程序提供的函数来实现。
即join
不能在(<*>)
、pure
和fmap
方面实现。
但是我上面提到的功能应用/mondad呢?
我知道join === (>>= id)
,并且对于归结为f x -> f x x
的函数monad,即通过将后者的一个参数作为前者的两个参数来提供二进制函数
。我可以用(<*>)
来表达吗?好吧,实际上我认为我可以:flip ($) <*> f === join f
不正确吗?flip ($) <*> f
不是没有(>>=)
/(=<<)
和return
的join
的实现吗?
但是,考虑到列表 applicative/monad,我可以表达join
而无需明确使用(=<<)
/(>>=)
和return
(甚至不是(<*>)
,fwiw):join = concat
; 所以可能实现join f = flip ($) <*> f
也是一种技巧,并没有真正显示我是否只依赖Applicative
或也依赖于Monad
。
当你实现这样的join
时,你正在使用超出Applicative
给你的函数类型的知识。这种知识被编码在($)
的使用中。这就是"应用程序"运算符,它是函数的核心。您的列表示例也会发生同样的事情:您正在使用concat
,它基于对列表性质的了解。
一般来说,如果你可以使用特定monad的知识,你可以表达任何幂的计算。例如,使用Maybe
,您可以只匹配其构造函数并以这种方式表达任何内容。当LYAH说monad比应用更强大时,它的意思是"作为抽象",而不是应用于任何特定的monad。
edit2:这个问题的问题在于它很模糊。它使用了一个根本没有定义的概念("更强大"),让读者猜测它的含义。因此,我们只能得到毫无意义的答案。当然,在使用我们可以使用的所有Haskell武器库时,任何东西都可以编码。这是一个空洞的说法。这不是问题所在。
据我所知,澄清的问题是:分别使用Monad/Applicative/Functor的方法作为原语,根本不使用显式模式匹配,是一类计算,因此可以严格表示为使用中的一组或另一组原语。现在,这个问题可以得到有意义的回答。
不过功能是不透明的。无论如何,不存在模式匹配。在不限制我们可以使用的内容的情况下,这个问题再次没有意义。然后,限制就变成了显式使用命名参数,即编程的要点风格,因此我们只允许自己以组合风格编码。
因此,对于列表,只有fmap
和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
。(->) r (a->b)
中的两个->
是相同的;功能很特殊。
(顺便说一句,我目前看不到如何在不实际编码join
的情况下显式编码列表的app
。使用列表推导等效于使用concat
;concat
不是join
的实现,而是join
)。
join f = f <*> id
很简单,所以毫无疑问。
(编辑:好吧,显然仍然存在疑问)。
(=<<) = (<*>) . flip
函数。就是这样。这就是函数 Monad 和 Applicative 函子是相同的。flip
是一个普遍适用的组合器。concat
不是。当然,那里有一定的融合,与函数。但是那里没有特定的函数操作函数(就像concat
是一个特定的列表操作函数),或者任何地方,因为函数是不透明的。
被视为一种特定的数据类型,它可以进行模式匹配。作为一个Monad,虽然它只知道>>=
和return
。concat
确实使用模式匹配来完成它的工作。id
没有。
id
这里类似于列表的[]
,而不是concat
。它的工作原理正是它的意思,即被视为应用函子或 Monad 的函数是相同的。当然,一般来说,Monad比Applicative拥有更大的权力,但这不是问题所在。如果你能表达对<*>
和[]
列表的join
,我会说这意味着它们对列表也有相同的权力。
在(=<<) = (<*>) . flip
中,flip
和(.)
对它们所应用的函数没有任何作用。因此,他们不了解这些函数的内部结构。就像,如果参数列表是例如,foo = foldr (x acc -> x+1) 0
将碰巧正确计算参数列表的长度[1,2]
.以此为基础,这样说是使用函数foo
的一些内部知识(与concat
通过模式匹配使用其参数列表的内部知识相同)。但仅仅使用基本的组合器,如flip
和(.)
等,不是。