Haskell令人惊讶的功能签名

Fra*_*ank 2 haskell

我是一个Haskell新手,请原谅我问这可能是显而易见的,但我对此感到惊讶:

len2 :: [a] -> Int
len2 xx = if xx == [] then 0 else 1 + (len2 (tail xx))
Run Code Online (Sandbox Code Playgroud)

给我:

No instance for (Eq a) arising from a use of ‘==’
Possible fix:
  add (Eq a) to the context of
    the type signature for len2 :: [a] -> Int
In the expression: xx == []
Run Code Online (Sandbox Code Playgroud)

我很惊讶,因为我认为Haskell可以判断一个列表是否在[]没有查看任何元素的情况下(在C++中,我会看到列表的大小为0,并将其留在那里,例如).

Dan*_*ner 9

(==)在这种情况下的类型是Eq a => [a] -> [a] -> Bool.希望你同意在看到任何参数之前Eq a约束是有意义的:如果你想检查两个任意列表是否相等,你必须知道如何检查元素是否相等.您可能希望,一旦您应用(==)了具体参数[],Eq a不需要约束,但实际上这是一个非常微妙的注意事项.也许部分评估可以产生没有约束的函数.但是现存的实现并没有尝试进行这种分析; 他们保留了Eq a => [a] -> Bool已经解决[a]Eq a不受约束的保守型.

一些选择:

  1. null :: [a] -> Bool而不是(==[]).

    len2 xx = if null xx then ... else ...
    
    Run Code Online (Sandbox Code Playgroud)
  2. 模式匹配,因此:

    len2 []     = ...
    len2 (x:xs) = ...
    
    Run Code Online (Sandbox Code Playgroud)

    作为奖励,这种方式可以避免调用令人讨厌的tail功能.

我会说选择(2)明显更具惯用性.