Section: 2.1 Polymorphic Types

第 2.1 节 多态类型

Start with some sample lists to use in test cases:

先准备一些用于测试用例的示例列表:

list1 :: [Int]
list1 = [1,2,3]
list2 :: [Char]         -- This is the really a String
list2 = ['a','b','c']   -- This is the same as "abc"; evaluate list2 and see.
list3 :: [[a]]          -- The element type of the inner list is unknown
list3 = [[],[],[],[]]   -- so this list can't be printed
list4 :: [Int]
list4 = 1:2:3:4:[]      -- Exactly the same as [1,2,3,4]; print it and see.

This is the length function. You can test it by evaluating expressions such as length list1. Function application is written by simple juxtaposition: f(x) in other languages would be f x in Haskell.

这就是 length 函数。你可以求值像 length list1 这样的表达式来测试它。

函数应用直接用并置书写:在其他语言中写作 f(x) 的东西,在 Haskell 中

写作 f x。

length :: [a] -> Int
length [] = 0
length (x:xs) = 1 + length xs

Function application has the highest precedence, so 1 + length xs is parsed as 1 + (length xs). In general, you have to surround non-atomic arguments to a function with parens. This includes arguments which are also function applications. For example, f g x is the function f applied to arguments g and x, similar to f(g,x) in other languages. However, f (g x) is f applied to (g x), or f(g(x)), which means something quite different! Be especially careful with infix operators: f x+1 y-2 would be parsed as (f x)+(1 y)-2. This is also true on the left of the =: the parens around (x:xs) are absolutely necessary. length x:xs would be parsed as (length x):xs.

函数应用的优先级最高,所以 1 + length xs 会被解析为 1 + (length xs)。

一般来说,传给函数的非原子参数必须用括号括起来,这包括本身也是函数

应用的参数。例如,f g x 是函数 f 作用于参数 g 和 x,类似于其他语言中的

f(g,x)。然而 f (g x) 是 f 作用于 (g x),即 f(g(x)),含义完全不同!

对中缀运算符要格外小心:f x+1 y-2 会被解析为 (f x)+(1 y)-2。

= 左边也是如此:(x:xs) 外面的括号是绝对必要的,

length x:xs 会被解析为 (length x):xs。

Also be careful with prefix negation, -. The application f -1 is f-1, not f(-1). Add parens around negative numbers to avoid this problem.

前缀取负运算符 - 也要小心。f -1 是 f-1,而不是 f(-1)。

给负数加上括号可以避免这个问题。

Here are some other list functions:

这里还有其他一些列表函数:

head :: [a] -> a -- returns the first element in a list (same as car in lisp)
head (x:xs) = x
tail :: [a] -> [a] -- removes the first element from a list (same as cdr)
tail (x:xs) = xs
null :: [a] -> Bool
null [] = True
null (x:xs) = False
cons :: a -> [a] -> [a]
cons x xs = x:xs
nil :: [a]
nil = []

Length could be defined using these functions. This is not good Haskell style but does illustrate these other list functions. Haskell programmers feel that the pattern matching style, as used in the previous version of length, is more natural and readable.

length 也可以用这些函数来定义。这不是好的 Haskell 风格,但确实演示了

这些其他列表函数的用法。Haskell 程序员觉得模式匹配的写法——就像前面

那个版本的 length 所用的——更自然、更易读。

length' :: [a] -> Int   -- Note that ' can be part of a name
length' x = if null x then 0 else 1 + length' (tail x)

A test case for length', cons, and nil

length'、cons 和 nil 的一个测试用例:

e1 = length' (cons 1 (cons 2 nil))

We haven't said anything about errors yet. Each of the following examples illustrates a potential runtime or compile time error. The compile time error is commented out so that other examples will compile; you can uncomment e2 and see what happens.

我们还没有谈到错误。下面的每个示例都演示了一种可能出现的运行时错误

或编译期错误。那个编译期错误被注释掉了,以便其他示例能够编译通过;

你可以取消 e2 的注释,看看会发生什么。

-- e2 = cons True False   -- Why is this not possible in Haskell?
e3 = tail (tail ['a'])  -- What happens if you evaluate this?
e4 = []       -- This is especially mysterious!

This last example, e4, is something hard to explain but is often encountered early by novices. We haven't explained yet how the system prints out the expressions you type in - this will wait until later. However, the problem here is that e4 has the type [a]. The printer for the list datatype is complaining that it needs to know a specific type for the list elements even though the list has no elements! This can be avoided by giving e4 a type such as [Int]. (To further confuse you, try giving e4 the type [Char] and see what happens.)

最后这个示例 e4 很难解释清楚,但新手常常很早就会遇到。我们还没有讲

系统是如何打印你输入的表达式的——这要留到后面再说。不过这里的问题是

e4 的类型是 [a]。列表数据类型的打印函数在抱怨:尽管这个列表没有任何

元素,它仍然需要知道列表元素的具体类型!给 e4 指定一个像 [Int] 这样的

类型就可以避免这个问题。(为了让你更困惑,试试把 e4 的类型定为 [Char],

看看会发生什么。)