此代码中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
(没有错误)。
答案 0 :(得分:11)
我的背景:我不太了解forall或Haskell隐含地拥有它们。
好的,请考虑id
,a -> a
的类型。 a
是什么意思,它来自哪里?定义值时,不能只使用未在任何位置定义的任意变量。您需要顶级定义,函数参数或where
子句,& c。通常,如果您在某处使用变量it must be bound。
类型变量也是如此,forall
是一种绑定类型变量的方法。在任何地方,您都会看到未明确绑定的类型变量(例如,class Foo a where ...
在类定义中绑定a
),它由forall
隐式绑定。
因此,id
的类型隐含forall a. a -> a
。这是什么意思?几乎就是这么说的。我们可以为所有可能的类型a -> a
获取类型a
,或者从另一个角度获取,如果您选择任何特定类型,您可以获得表示“所选类型的函数”的类型对自己“。后面的措辞听起来有点像定义一个函数,因此你可以认为forall
类似于类型的lambda抽象。
GHC在编译期间使用各种中间表示,并且它应用的一个转换是使函数的相似性更直接:隐式forall
s是显式的,并且任何地方的多态值都用于特定类型,它首先应用于类型参数。
我们甚至可以将forall
和lambdas都写成一个表达式。我会暂时滥用注释,并将forall a.
替换为/\a =>
以保持视觉一致性。在这种风格中,我们可以定义id = /\a => \(x::a) -> (x::a)
或类似的东西。因此,代码中的id True
这样的表达式最终会转换为类似id Bool True
的内容;只有id True
才会有意义。
正如您可以重新排序函数参数一样,您也可以重新排序类型参数,仅受限于类型参数必须在该类型的任何值参数之前出现的(相当明显的)限制。由于隐式forall
始终是最外层,因此GHC可能会在明确时选择任何所需的顺序。在正常情况下,这显然无所谓。
我不确定完全在这种情况下发生了什么,但根据评论我会猜测转换为使用显式类型参数和des do
表示法是,在某种意义上,彼此不了解,因此明确指定类型参数的顺序以确保一致性。毕竟,如果某些东西盲目地将两个类型的参数应用于表达式,那么该表达式的类型是forall a b. m a -> m b -> m b
还是forall b a. m a -> m b -> m b
是非常重要的!