首页 > 解决方案 > haskell 如何确定隐式 foralls 中类型变量的顺序?

问题描述

所以我最近才了解并开始使用TypeApplications,并且想知道我们通常如何知道我们正在分配的类型变量。我发现的文档TypeApplications提到:

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

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

但是,我还没有提到隐式 foralls 中类型变量的顺序是如何确定的。我尝试查看不同的示例,-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 中如何打印 foralls 的一个错误,因为否则类型应用程序似乎是使用 forall 的变量从右到左完成的,这与文档所说的相反:

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

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

标签: haskellghcghci

解决方案


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

不要使用:type

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

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

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

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

如果您申请,这是必要的(,)。唉,你没有,所以类型推断几乎完成了,它概括了所有自由变量,你得到,例如,

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

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

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

采用:type +v

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

你也可以在 GHCi 中获得这种行为——传递+v:type

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

看,没有{…}类型变量!

这个订单是从哪里来的?

GHC 用户指南关于可见类型应用程序的部分指出 :

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

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


推荐阅读