2014-02-14 105 views
1

以下编译没有警告或错误。为什么Haskell类型系统无法解决这个问题?

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绘制来自IntegersListinit预计有List
+0

Glorious Glasgow Haskell编译系统,版本7.4.1 – user3309706

回答

18

如果你看一下类型GHC已经推导出它,你可以看到

perfects :: forall a. (Eq a, Integral [a]) => [a] -> [[a]] 

所以,如果你有一个实例,使列表在Integral它的工作原理类。 而ghc不知道那不是你想要的。

如果你把预期的类型签名上perfects你会得到一个错误,或者你在你希望的方式使用perfects(改变mainprint (perfects 42))。

编辑这使你的代码做一些事情(无厘头):

module Main where 
factors n = [x | x<-[1..n], n `mod` x == 0] 
perfects n = [x | x <- [1..n], x == sum (factors (init x))] 

instance Num [a] 
instance Integral a => Integral [a] 
instance Real a => Real [a] 
instance Enum [a] where 
    enumFromTo _ _ = [] 

main = print (perfects 5) 

你写那么可能是你的原意。这就是为什么总是编写类型签名是很好的做法,以便编译器可以看到你的想法。或者至少,你应该检查推断的类型是你的意图。

+0

你在技术上是正确的,但是它写的函数是错误的,我仍然不明白为什么init允许用整数调用 – user3309706

+0

@ user3309706:这是因为Haskell整数文字被重载,所以'1'可以是一个列表,如果列表中有一个'Num'实例,同样'[1..n]'可以是给定适当实例的列表列表,所以没有什么能够阻止'x'成为一个列表。 – hammar

+0

好的,谢谢,这是否被认为是Haskell的(错误)功能?我清楚地认为这是一种错误! – user3309706

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 

简单:“您输入了错误的内容”。现在,Haskell:

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应用于参数02。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类来完成。

相关问题