Haskell函数调用时遇print引发Ambiguous type variable错误求助
这问题我之前在写Haskell泰勒展开相关代码的时候也碰到过,咱们一步步来理清楚问题出在哪,以及怎么解决它。
首先先还原你写的代码:
f x k = h x k h x i = x^i/factorial i factorial n = foldl (*) 1 [1..n]
你遇到的错误提示是:
:109:1: error:
• Ambiguous type variable ‘a0’ arising from a use of ‘print’ prevents the constraint ‘(Show a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Show Ordering -- Defined in ‘GHC.Show’
instance Show Integer -- Defined in ‘GHC.Show’
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
...plus 22 others
...plus 19 instances involving out-of-scope types (use -fprint-potential-instances to see them all)
• In a stmt of an interactive GHCi command: print it
错误原因拆解
这个错误的核心是GHC无法推断出f函数返回值的具体类型:
factorial n默认返回的是Integer类型(因为用了整数列表[1..n]和整数1做初始值)x^i的结果属于Num类型类,可以是Integer、Double等多种类型/操作要求操作数必须是Fractional类型(比如Double、Float),但此时GHC不知道你要把结果解析成哪种具体的Fractional类型;而print需要明确的Show实例,多种类型都满足条件,就出现了“类型模糊”的问题。
另外还有个隐藏问题:你当前的代码里x^i如果是Integer,factorial i也是Integer,但/不支持整数除法(整数除法要用div),所以就算解决了类型模糊问题,还会报另一个错误,咱们一起解决掉。
几种可行的解决方案
1. 给函数添加明确的类型注解
最稳妥的方式是直接给函数指定类型,比如如果你是要计算指数函数的泰勒展开项(从代码结构看大概率是这个场景),可以指定返回Double类型:
-- 明确f的输入输出类型 f :: Double -> Integer -> Double f x k = h x k -- h的类型会被自动推断,这里把factorial的Integer转成Double适配除法 h x i = x^i / fromIntegral (factorial i) -- 明确factorial的类型 factorial :: Integer -> Integer factorial n = foldl (*) 1 [1..n]
这里用fromIntegral把factorial返回的Integer转换成Double,满足/的类型要求,同时给f指定类型后,GHC就不会再纠结类型模糊的问题了。
2. 调用时临时添加类型标注
如果你不想修改函数定义,也可以在GHCi调用的时候明确指定结果类型:
> f 2.0 5 :: Double
这样GHC就知道要把结果解析成Double类型,就能正常执行print了。不过这种方式每次调用都要加标注,不如给函数加类型注解方便。
3. 修改factorial让它返回Fractional类型
如果你希望factorial直接返回浮点数,也可以调整它的定义:
factorial :: Fractional a => Integer -> a factorial n = foldl (*) 1.0 [1..fromIntegral n]
这样factorial会根据上下文返回对应的Fractional类型(比如Double),h x i里的除法就不需要额外转换了,调用时只要传入浮点数参数,GHC就能自动推断出类型。
内容的提问来源于stack exchange,提问作者user7826451

