问题 GHCi如何选择类型变量的名称?


使用交互式GHC解释器时,可以询问表达式的推断类型:

Prelude> :t map
map :: (a -> b) -> [a] -> [b]

它似乎从签名中获取类型变量的名称 map 是 定义 如

map :: (a -> b) -> [a] -> [b]
map _ []     = []
map f (x:xs) = f x : map f xs

在序曲中。这很有道理!我的问题是:当没有给出签名时,如何选择类型变量名称?

一个例子是

Prelude> :t map fst
map fst :: [(b, b1)] -> [b]

它选择了名字 b 和 b1。很明显,重命名必须发生,但只是从开始 ab,......会的

map fst :: [(a, b)] -> [a]

相反,我发现它更具可读性。


5328
2017-12-15 12:13


起源



答案:


据我了解, ghci 按照推断类型的顺序选择名称。它使用您提到的命名方案来确定结果的类型名称,即 [b] 因为这是在定义中指定的类型名称 map。然后它决定作为第一个参数的函数 map 应该返回类型的东西 b 也。

因此,要命名的其余类型变量是参数元组to中第二个元素的类型变量 fst,再次,它看着的定义 fst 决定使用哪个名称。的定义 fst :: (a, b) -> a所以 b 将是这里的首选名称,但从那以后 b 已经采取了,它附加一个 1 这样它就变成了 b1

我认为这个系统在你不处理任意类型的情况下具有优势,就像这里的情况一样。如果结果类型看起来像这样,例如:

castAdd :: (Num n, Num n1, Num n2) => n -> n1 -> n2

......它可以说比以下更具可读性:

castAdd :: (Num a, Num b, Num c) => a -> b -> c

...因为你可以主要依靠它 n# 表示数字类型,因为类的定义 Num 是 class Num n where ...

编辑:是的,我知道 castAdd 是不可能实现的,但它只是一个类型的例子。


13
2017-12-15 12:43



谢谢,这是一个很好的解释!我没想到你想要几个的情况 n要重命名但保持相关。 - Martin Geisler
这不是不可能的。 (unSafeCoerce或者只是简单的旧 _|_) - PyRulez


答案:


据我了解, ghci 按照推断类型的顺序选择名称。它使用您提到的命名方案来确定结果的类型名称,即 [b] 因为这是在定义中指定的类型名称 map。然后它决定作为第一个参数的函数 map 应该返回类型的东西 b 也。

因此,要命名的其余类型变量是参数元组to中第二个元素的类型变量 fst,再次,它看着的定义 fst 决定使用哪个名称。的定义 fst :: (a, b) -> a所以 b 将是这里的首选名称,但从那以后 b 已经采取了,它附加一个 1 这样它就变成了 b1

我认为这个系统在你不处理任意类型的情况下具有优势,就像这里的情况一样。如果结果类型看起来像这样,例如:

castAdd :: (Num n, Num n1, Num n2) => n -> n1 -> n2

......它可以说比以下更具可读性:

castAdd :: (Num a, Num b, Num c) => a -> b -> c

...因为你可以主要依靠它 n# 表示数字类型,因为类的定义 Num 是 class Num n where ...

编辑:是的,我知道 castAdd 是不可能实现的,但它只是一个类型的例子。


13
2017-12-15 12:43



谢谢,这是一个很好的解释!我没想到你想要几个的情况 n要重命名但保持相关。 - Martin Geisler
这不是不可能的。 (unSafeCoerce或者只是简单的旧 _|_) - PyRulez