fun 有三个参数和一个结果。所以最初编译器假设它们可能是不同的类型:
fun :: alpha -> beta -> gamma -> delta -- the type vars are placeholders
忽略守卫,看看方程的 rhs 结果:它们每个都必须是类型delta。所以从词项级方程中建立一系列类型级方程。在类型之间使用~ 表示它们必须相同。
- 第一个方程有结果
b,所以b的类型必须与结果相同,即beta ~ delta。
- 第三个方程有结果
c,所以c的类型必须与结果gamma ~ delta相同。
- 第二个等式有 rhs
+ 运算符 :: Num a => a -> a -> a。我们将回到Num a =>。这就是说+ 有两个参数和一个结果,它们都是相同的类型。该类型是 fun 的 rhs 的结果,因此必须是 delta。 + 的第二个参数是b,所以b 的类型必须与结果beta ~ delta 相同。我们已经从第一个等式中得到了这一点,所以这只是为了确认一致性。
-
+ 的第一个参数是a,所以a 的类型必须再次相同,alpha ~ delta。
我们有alpha ~ beta ~ gamma ~ delta。所以回到最初的签名(尽可能通用),用equals代替equals:
fun :: (constraints) => alpha -> alpha -> alpha -> alpha
约束
从运营商那里即时取货。
- 由于运营商
+,我们已经看到Num。
- 第一个等式的
a == c 为我们提供Eq。
- 第二个等式的
b < 0 为我们提供Ord。
- 实际上
0 的出现给了我们另一个Num,因为0 :: Num a => a 和< :: Ord a => a -> a -> Bool IOW 的参数< 必须是相同的类型和相同的约束。
所以在fun的类型前面堆积这些约束,消除重复
fun :: (Num alpha, Eq alpha, Ord alpha) => alpha -> alpha -> alpha -> alpha
这就是你最喜欢的编译器告诉你的吗?它可能使用类型变量a 而不是alpha。它可能没有显示Eq alpha。
消除不必要的超类约束
Main> :i Ord
...
class Eq a => Ord a where
...
Eq a => 告诉每个Ord 类型必须已经带有Eq。因此,在为fun 提供签名时,编译器会假设它的Eq 消失。
fun :: (Num a, Ord a) => a -> a -> a -> a
QED