这似乎适用于 GHCi 和 GHC。我将首先展示一个 GHCi 的例子。

给定的 i 类型推断如下:

Prelude> i = 1
Prelude> :t i
i :: Num p => p

鉴于 succ 是在 Enum 上定义的函数:
Prelude> :i Enum
class Enum a where
  succ :: a -> a
  pred :: a -> a
  -- …OMITTED…

并且 Num 不是 Enum 的“子类”(如果我可以使用该术语):
class Num a where
  (+) :: a -> a -> a
  (-) :: a -> a -> a
-- …OMITTED…

为什么 succ i 不返回错误?
Prelude> succ i
2 -- works, no error

我希望 :type i 被推断为:
Prelude> i = 1
Prelude> :type i
i :: (Enum p, Num p) => p

(我使用的是“GHC v.8.6.3”)

附加:

阅读@RobinZigmond 评论和@AlexeyRomanov 回答后,我注意到 1 可以解释为多种类型之一和多种类之一。
感谢@AlexeyRomanov 的回答,我对用于决定用于模糊表达式的类型的默认规则有了更多的了解。

但是,我不认为阿列克谢的回答完全解决了我的问题。我的问题是关于 i 的类型。这与 succ i 的类型无关。

这是关于 succ 参数类型(一个 Enum a )和 i 的明显类型(一个 Num a )之间的不匹配。

我现在开始意识到我的问题必须源于一个错误的假设:“一旦 i 被推断为 i :: Num a => a ,那么 i 就不能是别的”。因此我很困惑地看到 succ i 被评估没有错误。

除了明确声明的内容之外,GHC 似乎还推断 Enum a
x :: Num a => a
x = 1
y = succ x -- works

但是,当类型变量作为函数出现时,它不会添加 Enum a:
my_succ :: Num a => a -> a
my_succ z = succ z -- fails compilation

对我来说,附加到函数的类型约束似乎比应用于变量的类型约束更严格。

GHC 说 my_succ :: forall a. Num a => a -> a 并给出forall a 没有出现在 ix 的类型签名中,我认为这意味着 GHC 不会为 my_succ 类型推断更多类。

但这似乎又是错误的:我已经用以下内容(第一次输入 RankNTypes)检查了这个想法,显然 GHC 仍然推断 Enum a :
{-# LANGUAGE RankNTypes #-}

x :: forall a. Num a => a
x = 1
y = succ x

那么,函数的推理规则似乎比变量的推理规则更严格?

最佳答案

是的, succ i 的类型是按照您的预期推断的:

Prelude> :t succ i
succ i :: (Enum a, Num a) => a

这种类型是有歧义的,但是满足GHCi的the defaulting rules中的条件:



在这种情况下,只有一组: (Enum a, Num a)



保留这个组,因为 Num 是一个交互式类。



默认的默认类型列表 (sic) 是(添加了最后一个子句) default ((), [], Integer, Double)

因此,当您执行 Prelude> succ i 来实际评估此表达式时(注意 :t 不评估它获得的表​​达式), a 设置为 Integer (此列表中满足约束的第一个),结果打印为 2

您可以通过更改默认值来查看原因:
Prelude> default (Double)
Prelude> succ 1
2.0

对于更新的问题:


i 可以是其他任何东西(即任何不适合这种类型的东西),但它可以用于不那么通用(更具体)的类型: IntegerInt 。即使其中许多同时出现在一个表达式中:
Prelude> (i :: Double) ^ (i :: Integer)
1.0

并且这些用途不会影响 i 本身的类型:它已经被定义并且它的类型是固定的。到目前为止还好吗?

好吧,添加约束也使类型更具体,所以 (Num a, Enum a) => a(Num a) => a 更具体:
Prelude> i :: (Num a, Enum a) => a
1

因为当然满足 a 中的两个约束的任何类型 (Num a, Enum a) 都只满足 Num a



那是因为您指定了一个不允许它的签名。如果您不提供签名,则没有理由推断 Num 约束。但是例如
Prelude> f x = succ x + 1

将推断具有两个约束的类型:
Prelude> :t f
f :: (Num a, Enum a) => a -> a



由于 monomorphism restriction(默认情况下不在 GHCi 中),它实际上是相反的。你没有在这里遇到它实际上有点幸运,但答案已经足够长了。搜索这个词应该会给你解释。



那是红鲱鱼。我不确定为什么它在一种情况下而不是另一种情况下显示,但所有这些都在幕后使用 forall a:



(另外,你只需要 ExplicitForAll 而不是 RankNTypes 来写下 forall a. Num a => a 。)

关于haskell - 为什么 `succ i` 在 `i::Num a => a` (而不是 `Enum a` )中有效?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55308208/

10-13 09:28