所以我最近才了解并开始使用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)
那么类型变量是否总是按照它们在类型主体中从左到右的顺序出现(包括约束),总是以隐式的形式存在?
答案 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
推断类型的表达式。