了解Monad'>> ='函数中的所有内容?
在这个答案之后,我在我的程序中实现了一个通用升降功能:
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
成为任何类型( []
, Maybe
等)。
我现在正在研究>>=
在Monads中的定义:
class Applicative m => Monad m where
(>>=) :: forall a b. m a -> (a -> m b) -> m b
我无法理解这样的角色forall
在函数定义? 与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”语言扩展。 Haskell Prime对此扩展有一个描述
ExplicitForAll允许使用关键字'forall'来显式声明一个类型在其自由类型变量中是多态的。 它不允许写入任何不能写入的类型; 它只允许程序员明确说明(当前隐含的)量化。
这种语言扩展纯粹是可视化的,允许你明确地写出你以前不能的变量。 你可以简单地省略forall a b.
来自Monad
声明,并且该程序将在功能上保持完全相同。
说,用这个扩展名,你可以将liftTupe
改写为forall ab x. (x -> cx) -> (a, b) -> (ca, cb)
forall ab x. (x -> cx) -> (a, b) -> (ca, cb)
。 定义相同,功能相同,但读者现在可以清楚地看到,类型变量都定义在最上层。
你写的每一个函数都是通过它的类型变量被隐含地普遍量化的:
id :: a -> a -- this is actually universally quantified over a
id :: forall a. a -> a
id x = x
您可以使用ExplicitForall
语言附注实际打开此行为。
该属性非常有用,因为它限制了您编写仅适用于某些类型的代码。 想一想id
函数可以做些什么:它可以永远返回它的参数或循环。 这是它可以做的唯一两件事情,你可以根据它的类型签名来判断。
强制多态函数的所有实例的行为方式相同,不论类型参数如何,称为parametricity,在本博文中由Bartosz Milewski解释。 TL; DR是:使用参数性,我们可以保证在程序结构中的一些重新排序不会影响它的行为。 对于这个数学上更严格的处理,请参阅免费的定理! 由菲利普Wadler。
在Haskell的类型系统中的所有类型的变量由量化forall
。 但是,GHC可以在许多情况下推断量化,因此您无需在源代码中编写它们。
例如类型liftTuple
与forall
明确的是
liftTuple :: forall c a b. (forall x. x -> c x) -> (a, b) -> (c a, c b)
对于>>=
情况是一样的。