了解 Monad '>>=' 函数中的 forall?
Understanding forall in Monad '>>=' function?
根据 this 的回答,我在我的程序中实现了一个通用的提升函数:
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 -> c x
)?
基本上,当你不使用forall
时,所有类型在函数定义中都是全局的,这意味着它们都是在调用函数时推导出来的。使用 forall
您可以放弃使用 x
的函数,直到它被调用。
所以在第一个函数中你有一个接受 x
并给出 c x
的函数,然后你有一个包含 a
和 b
的元组并且你期望一个c a
和 c b
的元组。既然你已经说过第一个函数接受 x
,你可以使 x
变得与 a
相同,但它不会是 b
因为 x
为整个声明定义一次。所以你不能让函数同时接受 a
和 b
.
然而,在第二种情况下,x
范围仅限于采用 x
的函数。我们基本上是说有一个函数接受一些东西并用那个东西做 c
,它可以是任何类型。这使我们能够先向它提供 a
,然后再向其提供 b
,它就会工作。 x
现在外面不必是奇异的东西了。
您在 Monad
定义中看到的是 "ExplicitForAll" 语言扩展。 Haskell Prime 上有关于此扩展的描述
ExplicitForAll enables the use of the keyword 'forall' to explicitly state that a type is polymorphic in its free type variables. It does not allow any types to be written that cannot already be written; it just allows the programmer to explicitly state the (currently implicit) quantification.
这个语言扩展是纯视觉的,允许您明确地写出以前不能写出的变量。您可以简单地从 Monad
声明中省略 forall a b.
,程序将在功能上保持完全相同。
说,有了这个扩展,您可以将 liftTupe
重写为 forall a b x. (x -> c x) -> (a, b) -> (c a, c b)
。定义是一样的,作用也是一样的,但是读者现在会清楚的看到类型变量都定义在最顶层了。
Haskell 类型系统中的所有类型变量都由 forall
量化。但是,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
您实际上可以使用 ExplicitForall
语言编译指示打开此行为。
这个 属性 非常有用,因为它限制了您编写仅适用于某些类型的代码。想一想 id
函数可以做什么:它可以 return 它的参数或永远循环。这是它唯一可以做的两件事,你可以根据它的类型签名来计算。
强制所有多态函数实例以相同的方式运行,而不考虑类型参数称为 parametricity,Philip Wadler 在 this blog post by Bartosz Milewski. The TL;DR is: Using parametricity, we can guarantee that some reorderings in out program's structure do not affect it's behaviour. For a mathematically more rigorous treatment of this, see Theorems for free! 中对此进行了解释。
monad定义中的forall只是为了让全称量化更加明确。如果你有一个没有进一步限制的类型变量,它默认是通用的,即可以是任何东西。
那么让我们看看 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 无法表达这一点,因此我们将需要 rank2types 或 rankntypes。有了它我们可以写:
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)
根据 this 的回答,我在我的程序中实现了一个通用的提升函数:
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 -> c x
)?
基本上,当你不使用forall
时,所有类型在函数定义中都是全局的,这意味着它们都是在调用函数时推导出来的。使用 forall
您可以放弃使用 x
的函数,直到它被调用。
所以在第一个函数中你有一个接受 x
并给出 c x
的函数,然后你有一个包含 a
和 b
的元组并且你期望一个c a
和 c b
的元组。既然你已经说过第一个函数接受 x
,你可以使 x
变得与 a
相同,但它不会是 b
因为 x
为整个声明定义一次。所以你不能让函数同时接受 a
和 b
.
然而,在第二种情况下,x
范围仅限于采用 x
的函数。我们基本上是说有一个函数接受一些东西并用那个东西做 c
,它可以是任何类型。这使我们能够先向它提供 a
,然后再向其提供 b
,它就会工作。 x
现在外面不必是奇异的东西了。
您在 Monad
定义中看到的是 "ExplicitForAll" 语言扩展。 Haskell Prime 上有关于此扩展的描述
ExplicitForAll enables the use of the keyword 'forall' to explicitly state that a type is polymorphic in its free type variables. It does not allow any types to be written that cannot already be written; it just allows the programmer to explicitly state the (currently implicit) quantification.
这个语言扩展是纯视觉的,允许您明确地写出以前不能写出的变量。您可以简单地从 Monad
声明中省略 forall a b.
,程序将在功能上保持完全相同。
说,有了这个扩展,您可以将 liftTupe
重写为 forall a b x. (x -> c x) -> (a, b) -> (c a, c b)
。定义是一样的,作用也是一样的,但是读者现在会清楚的看到类型变量都定义在最顶层了。
Haskell 类型系统中的所有类型变量都由 forall
量化。但是,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
您实际上可以使用 ExplicitForall
语言编译指示打开此行为。
这个 属性 非常有用,因为它限制了您编写仅适用于某些类型的代码。想一想 id
函数可以做什么:它可以 return 它的参数或永远循环。这是它唯一可以做的两件事,你可以根据它的类型签名来计算。
强制所有多态函数实例以相同的方式运行,而不考虑类型参数称为 parametricity,Philip Wadler 在 this blog post by Bartosz Milewski. The TL;DR is: Using parametricity, we can guarantee that some reorderings in out program's structure do not affect it's behaviour. For a mathematically more rigorous treatment of this, see Theorems for free! 中对此进行了解释。
monad定义中的forall只是为了让全称量化更加明确。如果你有一个没有进一步限制的类型变量,它默认是通用的,即可以是任何东西。
那么让我们看看 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 无法表达这一点,因此我们将需要 rank2types 或 rankntypes。有了它我们可以写:
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)