以下编译时没有警告或错误。
factors n = [x | x<-[1..n], n `mod` x == 0]
perfects n = [x | x <- [1..n], x == sum (factors (init x))]
main = putStrLn "hello"
即使我犯了错误。
perfects n = [x | x <- [1..n], x == sum (factors (init x))] -- incorrect
perfects n = [x | x <- [1..n], x == sum (init (factors x))] -- should have been
静态类型检查救援在哪里?
我认为应该抓住错误的原因是:
factor
显然需要Integral
,因为其参数与mod
一起使用,而init
则返回List
x
来自List
Integers
而init
期待List
答案 0 :(得分:18)
如果你看一下ghc推断出的类型,你可以看到
perfects :: forall a. (Eq a, Integral [a]) => [a] -> [[a]]
因此,如果您有一个实例使列表位于类Integral
中,则它可以正常工作。
而ghc并不知道这不是你想要的。
如果您将预期的类型签名放在perfects
上,或者您按照预期的方式使用perfects
,则会收到错误(将main
更改为{{1} }})。
编辑这会让你的代码做一些事情(荒谬):
print (perfects 42)
所以你写的可能是你想要的。这就是为什么总是编写类型签名是好的,这样编译器就可以看到你的想法了。或者至少,您应该检查推断类型是否符合您的意图。
答案 1 :(得分:5)
奥古斯都说了什么,但我会补充一些想法。
这里的问题(没问题!)是Haskell做出权衡:更具灵活性,代价是具体的错误报告。由于Haskell只允许更多的东西,因此在很多情况下,与更主流的语言相比,它要么无法报告错误,要么报告的错误比其他语言报告的更加抽象。
例如,假设您打算键入1 + 2
,但是您手指并输入1 0 2
。以下是Python的响应方式:
Python 2.7.2 (default, Oct 11 2012, 20:14:37)
[GCC 4.2.1 Compatible Apple Clang 4.0 (tags/Apple/clang-418.0.60)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 1 0 2
File "<stdin>", line 1
1 0 2
^
SyntaxError: invalid syntax
简单:“你输错了。”现在哈斯克尔:
GHCi, version 7.6.3: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> 1 0 2
<interactive>:2:1:
No instance for (Num (a0 -> a1 -> t0)) arising from the literal `1'
Possible fix:
add an instance declaration for (Num (a0 -> a1 -> t0))
In the expression: 1
In the expression: 1 0 2
In an equation for `it': it = 1 0 2
<interactive>:2:3:
No instance for (Num a0) arising from the literal `0'
The type variable `a0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
instance Num Double -- Defined in `GHC.Float'
instance Num Float -- Defined in `GHC.Float'
instance Integral a => Num (GHC.Real.Ratio a)
-- Defined in `GHC.Real'
...plus three others
In the first argument of `1', namely `0'
In the expression: 1 0 2
In an equation for `it': it = 1 0 2
<interactive>:2:5:
No instance for (Num a1) arising from the literal `2'
The type variable `a1' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
instance Num Double -- Defined in `GHC.Float'
instance Num Float -- Defined in `GHC.Float'
instance Integral a => Num (GHC.Real.Ratio a)
-- Defined in `GHC.Real'
...plus three others
In the second argument of `1', namely `2'
In the expression: 1 0 2
In an equation for `it': it = 1 0 2
在Python中,1 0 2
是语法错误。在Haskell中,1 0 2
表示将函数1
应用于参数0
和2
。 Haskell的错误消息不是“你不能这样做”,而是“你没有告诉我如何将数字强制转换为双参数函数”(没有Num (a0 -> a1 -> t0)
的实例)。
在你的情况下,你设法编写了Haskell知道如何解释的东西,但意味着与你的意思截然不同的东西。作为程序员,这里最好的做法是使用描述你的意图的顶级类型声明,然后编译器可以检查这些。
最后注意事项:请记住,您可以在Haskell中执行此操作:
-- | Treat lists of numbers as numbers. Example:
--
-- >>> [1..3] * [2..5]
-- [2,3,4,5,4,6,8,10,6,9,12,15]
--
instance Num a => Num [a] where
xs + ys = [x + y | x <- xs, y <- ys]
xs * ys = [x * y | x <- xs, y <- ys]
xs - ys = [x - y | x <- xs, y <- ys]
negate xs = map negate xs
abs xs = map abs xs
signum xs = map signum xs
fromInteger x = [fromInteger x]
-- | Treat functions as numbers if they return numbers. The functions
-- must have the same argument type. Example:
--
-- >>> 1 0 2
-- 1
instance Num a => Num (r -> a) where
f + g = \r -> f r + g r
f * g = \r -> f r * g r
f - g = \r -> f r - g r
negate f = negate . f
abs f = abs . f
signum f = signum . f
fromInteger x = const (fromInteger x)
使用Integral
类可以完成同样的事情。