为什么这不起作用?
sum :: (Num a, Num b) => a -> b -> c
sum a b = a + b
当然,错误信息与签名有关,但我仍然不明白原因。
Couldn't match expected type ‘a’ with actual type ‘b’
‘b’ is a rigid type variable bound by
the type signature for:
sum :: forall a b c. (Num a, Num b) => a -> b -> c
‘a’ is a rigid type variable bound by
the type signature for:
sum :: forall a b c. (Num a, Num b) => a -> b -> c
In the second argument of ‘(+)’, namely ‘b’
In the expression: a + b
In an equation for ‘sum’: sum a b = a + b
我错过了什么?
答案 0 :(得分:8)
因为(+)
函数具有签名:
(+) :: Num a => a -> a -> a
这意味着(+)
函数要求操作数具有相同的类型,结果与操作数的类型相同。
您的签名意味着程序员可以选择任何Num
类型作为第一个操作数,任何Num
类型作为第二个操作数,然后构造任何类型。所以这意味着我可以将函数专门化为sum :: Int -> Float -> Char
,但没有定义(+)
。
我们可以使类型更灵活,例如使用fromIntegral :: (Integral a, Num b) => a -> b
:
integralSum :: (Integral i, Integral j, Num c) => i -> j -> c
integralSum x y = fromIntegral x + fromIntegral y
答案 1 :(得分:7)
对于不同的答案,让我们尝试忽略除了类型签名之外的所有内容。
sum :: (Num a, Num b) => a -> b -> c
这就是说如果我给你一些类型的值,你知道它是Num
(a
类型变量)的一个实例,我给你第二个值可以是一个不同的类型,但也是Num
(b
类型变量),那么你知道如何给我一个任何类型的值我要求的({{1} })。
也就是说,我会给你c
和(3%4 :: Rational)
你能不能给我(7.99 :: Double)
这是我的网络服务器的配置结构?表达式val :: Config
毕竟与您的类型签名相匹配。
答案 2 :(得分:5)
让我们看一下+
运算符的类型。我们可以使用ghci
命令在:t
中执行此操作:
Prelude> :t (+)
(+) :: Num a => a -> a -> a
请注意,两个操作数和返回值都必须具有相同的类型。这就是您得到编译器错误的原因。您允许sum
获取两种不同类型的操作数。因此,您可以将类型签名更改为
sum :: (Num a, Num a) => a -> a -> a
如果要添加不同类型的数字,在应用+
运算符之前,您需要使用其他逻辑将参数转换为相同类型。