作为一个 uni 赋值,我应该编写一个带有类型声明的函数:
pi_approx :: Int -> Double
Run Code Online (Sandbox Code Playgroud)
这是我的第一次尝试:
pi_approx :: Int -> Double
pi_approx x = let total = sum [1 / (y^2) | y <- [1..x]]
in sqrt (6 * total)
Run Code Online (Sandbox Code Playgroud)
这引发了以下错误:
pi_approx.hs:4:8: error:
* Couldn't match expected type `Double' with actual type `Int'
* In the expression: sqrt (6 * total)
In the expression:
let total = sum [1 / (y ^ 2) | y <- ...] in sqrt (6 * total)
In an equation for `pi_approx':
pi_approx x = let total = sum ... in sqrt (6 * total)
|
4 | in sqrt (6 * total)
| ^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)
我一步一步地试图理解为什么解释器把它当作一个 Int:
level1 :: Fractional a => a -> a
level1 x = 1 / (x^2)
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好。
level2 :: (Enum a, Fractional a) => a -> [a]
level2 x = [level1 y | y <- [1..x]]
Run Code Online (Sandbox Code Playgroud)
也符合预期。
level3 :: (Enum a, Fractional a) => a -> a
level3 x = sum (level2 x)
Run Code Online (Sandbox Code Playgroud)
没有 Int 被看到...
level4 :: (Enum a, Fractional a) => a -> a
level4 x = 6 * (level3 x)
Run Code Online (Sandbox Code Playgroud)
最后
level5 :: (Floating a, Enum a) => a -> a
level5 x = sqrt (level4 x)
Run Code Online (Sandbox Code Playgroud)
进入这个兔子洞后,我离我的答案更近了。我让它运行fromIntegral
pi_approx :: Int -> Double
pi_approx x = let total = sum [(fromIntegral 1) / ((fromIntegral y)^ (fromIntegral 2)) | y <- [1..x]]
in sqrt (6*total)
Run Code Online (Sandbox Code Playgroud)
但这让我无法理解导致第一个版本错误的原因。有人可以解释我缺少什么吗?为什么被sqrt (6*total)当作Int?
luq*_*qui 17
由于类型类的类型检查方式,您看到的类型错误并不是最有用的。
[ 1 / (y^2) | y <- [1..x] ]
Run Code Online (Sandbox Code Playgroud)
因此,这可能不足为奇,因为x是Int,[1..x]是Ints的列表,所以y是Int。也不是令人惊讶的y^2是Int。GHC 失去我们的地方是当它决定因此1 / (y^2)是一个Int.
原因是这样的:除法运算符的类型是
(/) :: (Fractional a) => a -> a -> a
Run Code Online (Sandbox Code Playgroud)
也就是说,它在两边取相同的类型,并返回它。因此,一旦y^2知道的类型是Int,我们就推断出1并且整个表达式Int也是。 只有稍后,在约束检查阶段,GHC 才会确定Int是Fractional,当然不是。例如:
recp :: Int -> Int
recp x = 1 / x
Run Code Online (Sandbox Code Playgroud)
会给你一个更好的错误
• No instance for (Fractional Int) arising from a use of ‘/’
• In the expression: 1 / x
In an equation for ‘recp’: recp x = 1 / x
Run Code Online (Sandbox Code Playgroud)
但是在检查您的函数时并没有走那么远,因为首先出现了类型统一失败。
有时,如果您无法找出错误,删除签名并查看推断出的类型会有所帮助(更多情况下,添加更多类型签名会很有帮助,但并非总是如此)
ghci> :t pi_approx
pi_approx :: (Floating a, Enum a) => a -> a
Run Code Online (Sandbox Code Playgroud)
如果没有签名,该函数实际上会进行类型检查。它也有效:
ghci> pi_approx 100
3.1320765318091053
Run Code Online (Sandbox Code Playgroud)
这里100默认为 aDouble来满足Fractional约束,并且整个定义将所有内容都作为Doubles。
只是它不接受Ints:
ghci> pi_approx (100 :: Int)
<interactive>:1:1: error:
• No instance for (Floating Int) arising from a use of ‘pi_approx’
• In the expression: pi_approx (1 :: Int)
Run Code Online (Sandbox Code Playgroud)