Monad比Applicative更强大吗?

时间:2016-01-21 19:27:45

标签: haskell functional-programming monads functor applicative

我看了past discussion,但看不出为什么任何答案都是正确的。

应用型

<*> :: f (a -> b) -> f a -> f b

单子

(>>=) :: m a -> (a -> m b) -> m b

因此,如果我做得对,那么声称>>=只能假设存在<*>

而无法写出

好吧,我们假设我有<*>

我想创建>>=

所以我有f a

我有f (a -> b)

现在看一下,f (a -> b)可以写成(a -> b)(如果某个东西是x,y,z的函数 - 那么它也是x,y的函数)。

因此,从<*>的存在,我们得到(a -> b) -> f a -> f b,这又可以写成((a -> b) -> f a) -> f b,可以写成(a -> f b)

我们有f a,我们有(a -> f b),我们知道<*>会产生f b,所以我们得到:

f a -> (a -> f b) -> f b

这是一个monad。

实际上,用更直观的语言:在实施<*>时,我从(a -> b)中提取f(a -> b),我从a中提取f a,然后我在(a -> b)上应用a并获取b,我将其f包裹起来,最终得到f b

所以我创建>>=几乎一样。在(a -> b)上应用a并获取b之后,再做一步并用f包裹它,这样我就会返回f b,因此我知道我有一个(a -> f b)函数class Country(models.Model): name = models.CharField(verbose_name="Name of country", max_length=100, default="Australia") number = models.IntegerField(verbose_name="number of country", default="1") def __unicode__(self): return self.name class World(models.Model): country = models.ManyToManyField(Country) name = models.CharField(verbose_name="New Map", max_length=100) def __unicode__(self): return self.name

2 个答案:

答案 0 :(得分:12)

  

现在,当您查看它时,f(a -> b)可以写为(a -> b)

没有。它不能。在这一点上,你的直觉(危险地)远远不够。这就像说锤子是完美的驱动螺钉,因为它已经适用于钉子*。你不能简单地在这里删除f,它是**类型的一部分。

相反,让我们直截了当地了解事实。 Applicative有三个相关的函数,计算Functor的{​​{1}}:

fmap

这是另一个事实:你可以用fmap :: Functor f => (a -> b) -> f a -> f b pure :: Applicative f => a -> f a (<*>) :: Applicative f => f (a -> b) -> f a -> f b 定义bind((>>=)),反之亦然:

join
  

是你在这里提供的加入和绑定的实现Monad定义的一部分,还是只加入和绑定Monad定义的一部分签名? [...]所以现在我问自己他们为什么要打扰。

这些当然不是官方定义,因为它们永远不会终止。如果你想让它成为一个monad,你必须为你的类型定义join :: Monad m => m (m a) -> m a join k = k >>= id (>>=) :: Monad m => m a -> (a -> m b) -> m b k >>= f = join (fmap f k)

(>>=)
  

此外,您的连接定义使用不在Monad界面中的ID,为什么它在数学上是合法的?

首先,为任何类型定义instance Monad YourType where k >>= f = ... 。其次,monad的数学定义实际上是通过id :: a -> a。所以它“更”合法。但最重要的是,我们可以用join(运动)来定义monad定律。

如果我们通过join创建join,我们也可以创建绑定。如果我们无法通过Applicative方法创建join,我们也无法导出Applicative。而bind的类型实际上很明显我们无法从join派生出来:

Applicative

加入可以删除其中一个join :: Monad m => m (m a) -> m a -- ^ ^ ^ 图层。让我们检查是否可以在其他方法中执行相同的操作:

m
fmap  :: Functor f     =>   (a -> b) -> f a -> f b
                          ^                    ^
                        0 here              1 here
pure  :: Applicative f =>                 a -> f a
                                          ^  | ^
                                      0 here | 1 here

答案是否定的:(<*>) :: Applicative f => f (a -> b) -> f a -> f b ^ ^ 1 here 1 here 给出的任何工具都不能让我们将多个Applicative合并为一个。{1}}。这也是在Typeclassopedia之后写在另一个问题的引用段落之后的内容:

  

要从不同的角度看Monad的强大功能,让我们看看如果我们尝试按m(>>=)和{{1}实施fmap会发生什么}}。我们获得pure类型的值(<*>)x类型的函数m a,因此我们唯一能做的就是将k应用于{ {1}}。当然,我们不能直接申请;我们必须使用a -> m b将其提升到k。但是x的类型是什么?好吧,它是fmap。因此,在我们将其应用于m后,我们会留下fmap k类型的东西 - 但现在我们被卡住了;我们真正想要的是m a -> m (m b),但是没有办法从这里到达那里。我们可以使用x添加m (m b),但我们无法将多个m b合并为一个。

请注意,m无法完全摆脱pure,这将是一次完全提取,并且 - 取决于其他一些功能 - {{3}的一项功能}。无论哪种方式,确保你不要让你的直觉误入歧途;信任并使用类型。

*这种比较有点不好,因为你实际上可以尝试用锤子将螺钉拧入一块木头。所以想想你想要钉入的塑料螺丝,橡胶锤和碳钢板。祝你好运。

**嗯,你可以删除它,但随后类型会发生巨大变化。

***鉴于mjoin相当于权力,任何m使用公式只能使用(>>=)转换为一个,它们是当然还有数学声音。

答案 1 :(得分:10)

  

现在,当您查看它时,f (a -> b)可以写为(a -> b)

每个人都已经做出了解释,这不是事实。让我向你证明。

如果我们真的拥有你所声明的内容,那么我们应该能够编写一个函数

expose :: f (a -> b) -> (a -> b)

此外,对于我们喜欢的任何具体数据类型,将其称为F,我们应该能够编写

expose_F :: F (a -> b) -> (a -> b)
expose_F = expose

我们只担心撰写expose_F,因为如果我们可以证明expose_F无法为某些F撰写,那么我们肯定会证明expose无法写入。{ / p>

我来试试F。这肯定是非直觉的感觉,因为我打算用它来打破直觉,但我很高兴终日确认没有任何有趣的事情

data F a = F

确实,它是Functor

instance Functor F where
  fmap _ F = F

Applicative就此而言

instance Applicative F where
  pure _ = F
  F <*> F = F

即使是Monad

instance Monad F where
  return _ = F
  F >>= _ = F

您可以自己验证所有这些类型检查。关于F,没有任何错误。

那么正确关于什么,F?我为什么选择它?好F很有意思,因为F a的值无法包含与其中a相关的任何内容。通常人们喜欢将数据类型(或Functor s)称为“容器”或“盒子”。 F迫使我们记住,在某种意义上,一个0英寸深的盒子仍然是一个盒子。 [0]

所以我们肯定不能写

expose_F :: F (a -> b) -> (a -> b)

有很多方法可以证明这一点。最简单的是吸引我的假设,例如,您认为没有coerce功能。但是,如果我们有expose_F就会有!

coerce :: a -> b
coerce = expose_F F

更具体地说,让我介绍另一种病理类型(我再次向你保证完全没问题)

data Void

Void构造函数,因此我们想说Void 没有成员。它不可能存在。但我们可以使用expose_F创建一个。

void :: Void
void = expose_F F ()

在Haskell中,我们在技术上不够健全以执行上述证明。如果你不喜欢我谈论不可能性的方式那么你可以通过一个方便的无限循环,随意调用

来想出你喜欢的任何类型。
 error "Madness rides the star-wind... claws and teeth sharpened on centuries of corpses... dripping death astride a bacchanale of bats from nigh-black ruins of buried temples of Belial..."

或者可能是一个不起眼的undefined。但这些都在疯狂的道路上。

没有expose_F,因此没有expose

[0]并且完全清楚,将数据类型视为盒子通常是有缺陷的。 Functor的实例往往是“类似盒子”,但这是另一种有趣的数据类型,很难将其视为一个盒子

data Unbox a = Unbox (a -> Bool)

除非你认为Unbox a是一个包含Bool否定 a或类似内容的框。也许是IOU a