此代码中forall
的用途是什么?
class Monad m where
(>>=) :: forall a b. m a -> (a -> m b) -> m b
(>>) :: forall a b. m a -> m b -> m b
-- Explicit for-alls so that we know what order to
-- give type arguments when desugaring
(省略了一些代码)。这是Monads的代码。
我的背景:我不太了解forall
,也不知道Haskell什么时候隐含了它们。
此外,它可能并不重要,但GHCi允许我在给>>
一个类型时省略forall
:
Prelude> :t (>>) :: Monad m => m a -> m b -> m b
(>>) :: Monad m => m a -> m b -> m b
:: (Monad m) => m a -> m b -> m b
(没有错误)。
我的背景:我真的不了解forall,也不了解Haskell何时隐式地拥有它们。
好的,考虑id
、a -> a
的类型。a
是什么意思?它来自哪里?当你定义一个值时,你不能只使用没有在任何地方定义的任意变量。您需要一个顶级定义、函数参数或where
子句,&c.一般来说,如果你使用一个变量,它必须绑定在某个地方。
类型变量也是如此,forall
就是绑定类型变量的一种方法。在任何您看到的类型变量没有显式绑定的地方(例如,class Foo a where ...
在类定义中绑定a
),它都是由forall
隐式绑定的。
因此,id
的类型隐含地为forall a. a -> a
。这是什么意思?几乎和上面说的一样。我们可以为所有可能的类型a
获得类型a -> a
,或者从另一个角度来看,如果您选择任何特定类型,您可以获得表示"从您选择的类型到其自身的函数"的类型。后一种措辞听起来应该有点像定义函数,因此您可以将forall
视为类似于类型的lambda抽象。
GHC在编译过程中使用各种中间表示,它应用的转换之一是使与函数的相似性更直接:隐式forall
s被显式化,在任何为特定类型使用多态值的地方,它首先被应用于类型参数。
我们甚至可以将forall
s和lambdas写成一个表达式。我将暂时滥用表示法,并将forall a.
替换为/a =>
以获得视觉一致性。在这种风格中,我们可以定义id = /a => (x::a) -> (x::a)
或类似的东西。因此,代码中类似id True
的表达式最终会被翻译成类似id Bool True
的表达式;仅仅CCD_ 25将不再有意义。
正如您可以重新排序函数参数一样,您也可以重新排序类型参数,只受类型参数必须在该类型的任何值参数之前的限制(相当明显)。由于隐式forall
总是最外层,GHC在使其显式时可能会选择它想要的任何顺序。在正常情况下,这显然无关紧要。
我不确定在这种情况下到底发生了什么,但根据评论,我猜在某种意义上,向使用显式类型参数的转换和对do
表示法的淡化是相互不知道的,因此显式指定了类型参数的顺序以确保一致性。毕竟,如果有东西盲目地将两个类型参数应用于一个表达式,那么该表达式的类型是forall a b. m a -> m b -> m b
还是forall b a. m a -> m b -> m b
就非常重要了!