Haskell函数加载时出现类型错误,但内联执行正常
powersOfx函数的类型签名不匹配问题 嘿,作为Haskell新手遇到类型相关的问题太正常啦,咱们一步步把这个问题捋明白!
首先,你在WinGHCi里执行这段内联代码完全没问题:
> let x = 0.5 > let n = 5 > map (\y->(x**y)) [0..n] [1.0,0.5,0.25,0.125,6.25e-2,3.125e-2] -- 注意这是1/2的幂!
但当你把逻辑写成带类型签名的函数时就报错了:
powersOfx :: (Integral a, Floating b) => a -> b -> [b] powersOfx n x = map (\y->(x**y)) [0..n]
加载时的错误提示是:
Couldn't match expected type ‘b’with actual type ‘a’ ‘a’is a rigid type variable bound by the type signature for: powersOfx :: forall a b. (Integral a, Floating b) => a -> b -> [b]
问题出在哪儿?
核心问题是(**)这个运算符的类型约束:它要求两个参数必须是相同类型,而且这个类型得属于Floating类。你可以看一下它的类型签名:
(**) :: Floating a => a -> a -> a
你在函数里写了x**y,其中x是Floating b类型,但y来自[0..n],n是Integral a类型,所以y的类型是a——这就导致了类型不匹配,Haskell没办法自动把Integral类型的y转换成Floating类型的b。
两种修复方案
1. 保留Integral约束,转换参数类型
修改函数体,用fromIntegral把整数列表里的元素转换成b类型,这样y就和x类型一致了,同时保留你想要的Integral a约束:
powersOfx :: (Integral a, Floating b) => a -> b -> [b] powersOfx n x = map (\y -> x ** fromIntegral y) [0..n]
fromIntegral可以把任何Integral类型的值转换成Num类的类型,而Floating属于Num的子类,所以这个转换是完全合法的。
2. 调整签名匹配自动推导结果
你注释掉签名后GHC自动推导的类型是powersOfx :: (Floating b, Enum b) => b -> b -> [b],这个类型是合理的:因为[0..n]要求n属于Enum类,而Double这类Floating类型本身就是Enum的实例。如果你能接受n是浮点类型的话,直接用这个签名就行,函数体不用修改。
关于你提到的“模糊类型变量”问题
这个问题和当前的类型不匹配是相关的:当类型变量没有足够的约束时,GHC不知道该选择哪个具体类型。比如用第一种修复方案后,你调用powersOfx 5 0.5时,GHC能明确a是Int、b是Double,自然就不会出现模糊变量的错误了。
内容的提问来源于stack exchange,提问作者Dave

