Haskell 理解Monad中的所有内容'&燃气轮机&燃气轮机=';功能?

Haskell 理解Monad中的所有内容'&燃气轮机&燃气轮机=';功能?,haskell,monads,forall,Haskell,Monads,Forall,以下是我在程序中实现的通用提升功能: liftTupe :: (x -> c x) -> (a, b) -> (c a, c b) --This will error liftTuple :: (forall x. x -> c x) -> (a, b) -> (c a, c b) 我理解,在这种情况下,forall使x成为任何类型([],可能等) 我现在正在研究monad中>=的定义: class Applicative m => Monad m w

以下是我在程序中实现的通用提升功能:

liftTupe :: (x -> c x) -> (a, b) -> (c a, c b) --This will error
liftTuple :: (forall x. x -> c x) -> (a, b) -> (c a, c b)
我理解,在这种情况下,
forall
使
x
成为任何类型(
[]
可能
等)

我现在正在研究monad中
>=
的定义:

class Applicative m => Monad m where
   (>>=) :: forall a b. m a -> (a -> m b) -> m b

很抱歉,我无法理解此
在函数定义中的作用?因为,与
liftTuple
不同,它没有绑定到特定的函数(
x->cx
)?

基本上,当您不使用
forall
时,所有类型在函数定义中都是全局的,这意味着它们都是在调用函数时推导出来的。使用
forall
可以放弃函数的调用
x
,直到函数本身被调用为止

因此,在第一个函数中,你有一个取
x
并给出
cx
的函数,然后你有一个
a
b
的元组,你期望一个
ca
cb
的元组。由于您已经说过第一个函数接受
x
,因此可以使
x
a
相同,但它不会是
b
,因为
x
在整个声明中定义了一次。因此,您无法使函数同时接受
a
b

但是,在第二种情况下,
x
范围仅限于执行
x
的函数。我们基本上是说,有一个函数,它接受某个东西,并用该东西生成
c
,它可以是任何类型。这使我们能够先将
a
馈送到它,然后再将
b
馈送到它,它就会工作<代码>x
现在在外部不一定是奇异的

您在Monad的定义中看到的是“ExplicitForAll”语言扩展。此扩展的上有一个描述

ExplicitForAll允许使用关键字'forall'显式声明类型在其自由类型变量中是多态的。它不允许写入任何无法写入的类型;它只允许程序员显式地声明(当前是隐式的)量化

这种语言扩展纯粹是可视化的,允许您显式地写出以前无法写出的变量。您只需从
Monad
声明中省略
所有ab.
,程序的功能将保持完全相同


比方说,通过这个扩展,您可以将
liftTupe
重写为所有abx的
。(x->c x)->(a,b)->(c a,c b)
。定义是一样的,功能也是一样的,但读者现在会清楚地看到类型变量都是在最顶层定义的。

Haskell类型系统中的所有类型变量都是通过一个
来量化的。然而,在许多情况下,GHC可以推断量化,因此您不需要在源代码中编写它们

例如,
liftTuple
forall
的类型是显式的

liftTuple :: forall c a b. (forall x. x -> c x) -> (a, b) -> (c a, c b)

对于
>>=
来说,情况是相同的

您编写的每个函数都在其类型变量上进行隐式通用量化:

id :: a -> a           -- this is actually universally quantified over a
id :: forall a. a -> a
id x = x
实际上,您可以使用pragma语言启用此行为

此属性非常有用,因为它限制您编写仅适用于某些类型的代码。想想
id
函数可以做什么:它可以返回参数,也可以永远循环。这是它唯一能做的两件事,您可以根据它的类型签名来确定这一点


强制多态函数的所有实例以相同的方式运行,而不管类型参数是什么,这被称为parametricity,Bartosz Milewski在博客文章中对此进行了解释。TL;DR is:使用参数化,我们可以保证out程序结构中的一些重新排序不会影响其行为。对于数学上更严格的处理,请参见Philip Wadler。

单子定义中的所有内容只是为了使通用量化更明确。如果您的类型变量没有进一步的限制,那么默认情况下它是通用的,即可以是任何类型的变量

让我们看看forall的两种用法之间的区别,以及haskell对它们的看法:

隐含的:

foo ::  (x -> f x) -> a -> b -> (f a, f b)
-- same as
foo :: forall f x a b . (x -> f x) -> a -> b -> (f a, f b)
-- our function is applied to a, so x is equal to a
foo :: forall f x a b . (x ~ a) => (x -> f x) -> a -> b -> (f a, f b)
-- our function is also applied to b, so x is equal to b
foo :: forall f x a b . (x ~ a, x ~ b) => (x -> f x) -> a -> b -> (f a, f b)
哦,(x~a,x~b)需要(a~b)。这将在没有注释的情况下进行推断,但由于我们显式地使用了不同的类型变量,所以一切都会崩溃。为了解决这个问题,我们需要f在函数中保持多态性

标准haskell无法表达这一点,因此我们需要RANK2TYPE或RANKNTYPE。有了这些,我们可以写:

foo ::  (forall x . x -> f x) -> a -> b -> (f a, f b)
注意,forall是函数类型的一部分。这样,它在我们的函数中保持多态性,我们可以将其应用于不同的类型,而不会导致任何问题

请注意,我们也可以这样做:

foo :: Monad m => a -> b -> (m a, m b)
foo a b = (return a, return b)

不知道为什么所有的答案都这么冗长。用一句话来说:你是对的;它不会改变任何东西,只是为了显式地存在>=
的code>绑定到一个特定的函数&function
ma->(a->mb)->mb
。您也可以使用不带函数的
for all
,例如
[]::for all a。[a] 
空::用于所有f a。备选方案f=>f a
@Ryan,当我得到答案:“因为所有东西都是隐式限定的。”我的后续问题通常是“为什么?”。谢谢Malcolm,这是一个很好的答案。实际上,你不需要
Monad
,来自
Applicative
就足够了。事实上,
指向的
更弱。很好!applicative在语言标准中是monad的一个超类吗?是的,我相信是从GHC 7.8开始的。如果你想查找它,它被称为AMP或ApplicationMonad建议。