haskell如何确定隐式forall中类型变量的顺序?

时间:2018-10-20 03:05:39

标签: haskell ghc ghci

所以我最近才了解并开始使用TypeApplications,并且想知道我们通常如何知道要分配的类型变量。 The documentation on TypeApplications I found提到:

  

实例化类型变量使用什么顺序?

     

类型变量从左到右的顺序出现在forall中。当在类型变量级别完成实例化时,这是最合乎逻辑的顺序。嵌套的forall的工作方式略有不同,但在具有多个变量的单个forall位置,发生了从左到右的顺序。 (有关嵌套的forall,请参见下文)。

但是我还没有提到如何确定隐式forall中类型变量的顺序。我尝试使用-fprint-explicit-foralls查看不同的示例,以查看是否存在简单的模式,但是在不同版本的ghci中,我得到了不同的结果。 :/

在ghci版本8.0.2中,我得到:

> :t (,,)
(,,) :: forall {c} {b} {a}. a -> b -> c -> (a, b, c)

在ghci版本8.4.3中,我得到:

> :t (,,)
(,,) :: forall {a} {b} {c}. a -> b -> c -> (a, b, c)

再说一遍,也许这仅仅是在8.0.2中打印allall的方式中的一个错误,因为否则类型应用程序似乎是使用forall的变量从右到左完成的,这与文档中所说的相反:

> :t (,,) @Bool
(,,) @Bool :: forall {c} {b}. Bool -> b -> c -> (Bool, b, c)

那么类型变量是否总是按照它们在类型主体中从左到右的顺序出现(包括约束),总是以隐式的形式存在?

1 个答案:

答案 0 :(得分:16)

TL; DR:类型变量顺序由第一次从左到右的相遇确定。如有疑问,请使用:type +v

请勿使用:type

在这里使用:type会产生误导。 :type推断整个表达式的类型。因此,当您编写:t (,)时,类型检查器会查看

(,) :: forall a b. a -> b -> (a, b)

并使用新的类型变量实例化所有的变量

(,) :: a1 -> b1 -> (a1, b1)

,如果您要应用(,),这是必需的。 las,您不需要,所以类型推断几乎完成了,它可以概括所有可用变量,例如,

(,) :: forall {b} {a}. a -> b -> (a, b)

此步骤不能保证free变量的顺序,并且编译器可以自由更改。

还要注意,它写的是forall {a}而不是forall a,这意味着您不能在此处使用可见类型的应用程序。

使用:type +v

但是您当然可以使用(,) @Bool –但是这里的类型检查器会以不同的方式对待第一个表达式,并且不执行此实例化/泛化步骤。

您也可以在GHCi中获得这种行为– pass +v to :type

:type +v (,)
(,) :: forall a b. a -> b -> (a, b)
:type +v (,) @Bool
(,) @Bool :: forall b. Bool -> b -> (Bool, b)

看,类型变量周围不要{…}

此订单来自哪里?

the GHC user's guide section on visible type application指出:

  

如果标识符的类型签名不包含显式的forall,则类型变量参数将按从左到右的顺序显示,变量在类型中出现。因此,foo :: Monad m => a b-> m(a c)的类型变量将按m,a,b,c排序。

这仅适用于具有显式类型签名的事物。推断类型中的变量没有确定的顺序,但是您也不能使用VisibleTypeApplication推断类型的表达式。