了解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)
对于>>=情况是一样的。
