Introduction
引言
This is a programming supplement to A Gentle Introduction to Haskell, version 98, by Hudak, Peterson, and Fasel. This supplement augments the tutorial by providing executable Haskell programs which you can run and experiment with. All program fragments in the tutorial are found here, as well as other examples not included in the tutorial.
这是一份编程补充材料,配套 Hudak、Peterson 和 Fasel 合著的《A Gentle
Introduction to Haskell》98 版。本补充材料通过提供可运行的 Haskell
程序来充实这篇教程,你可以运行并试验这些程序。教程中的全部程序片段
都能在这里找到,此外还有一些教程未收录的示例。
You should have a copy of both the Gentle Introduction and the Haskell 98 report itself to make full use of this tutorial. Although the Gentle Introduction is meant to stand by itself, it is often easier to learn a language through actual use and experimentation than by reading alone. Once you finish this introduction, we recommend that you proceed section by section through the Gentle Introduction and after having read each section go back to this online tutorial. You should wait until you have finished the tutorial before attempting to read the report. We suggest that you run this code using Hugs, a small Haskell interpreter. Everything is available for download at haskell.org.
要充分利用本教程,你最好同时备有《Gentle Introduction》和 Haskell 98
报告本身。虽然《Gentle Introduction》可以独立阅读,但通过实际使用和
试验来学习一门语言,通常比单纯阅读更容易。读完这篇引言之后,我们建议
你逐节阅读《Gentle Introduction》,每读完一节就回到这个在线教程。
在你读完本教程之前,先不要急着去读报告。我们建议你用 Hugs(一个小型
Haskell 解释器)来运行这里的代码。以上内容都可以在 haskell.org 下载。
This tutorial does not assume any familiarity with Haskell or other functional languages. Throughout the online component of this tutorial, we try to relate Haskell to other programming languages and clarify the written tutorial through additional examples and text.
本教程不要求你事先熟悉 Haskell 或其他函数式语言。在整个在线教程中,
我们尽量把 Haskell 与其他编程语言联系起来,并用额外的示例和文字来
阐明书面的教程。
Using Hugs
使用 Hugs
If you are using this with Hugs, here are the commands you will need to use. Start up hugs and change to the directory containing these files using :cd. Load each part using :l part1 (or whatever part is next). Inside each part, just type an expression to evaluate it. The expressions that are meant to be evaluated are e1, e2, and so on so if you type e1 you will see the result of evaluating e1. You can also type more complex expressions if you want. You may also edit these .lhs files (make a fresh copy of them if you want before you start); if you change the .lhs file you have to type :r to reload the file into hugs. If you made any mistakes in the program you'll have to fix them to get the :r to work.
如果你使用 Hugs,下面是你会用到的命令。启动 hugs,并用 :cd 切换到
存放这些文件的目录。用 :l part1(或下一部分对应的名字)加载每一部分。
在每一部分里,直接输入一个表达式即可对其求值。那些被设计用来求值的
表达式命名为 e1、e2 等,所以输入 e1 就会看到 e1 的求值结果。当然
你也可以输入更复杂的表达式。你还可以编辑这些 .lhs 文件(开始前最好
先复制一份);修改了已加载的 .lhs 文件后,必须输入 :r 把文件重新
载入 hugs。如果程序中改出了错误,就得先修正错误,:r 才能成功。
Organization of the Online Tutorial
在线教程的组织方式
This online tutorial is divided into a series of file. Each file covers one or more sections in the written tutorial. Each file is a single Haskell program. Comments in the program contain the text of the online tutorial.
本在线教程划分为一系列文件。每个文件覆盖书面教程中的一个或多个小节。
每个文件都是一个独立的 Haskell 程序。程序中的注释就是本在线教程的正文。
To create useful, executable examples of Haskell code, some language constructs need to be revealed well before they are explained in the tutorial. We attempt to point these out when they occur. Some small changes have been made to the examples in the written tutorial; these are usually cosmetic and should be ignored. Don't feel you have to understand everything on a page before you move on -- many times concepts become clearer as you move on and can relate them to other aspect of the language.
为了构造有用的、可运行的 Haskell 示例,一些语言构造需要在教程正式
讲解之前就较早地出现。我们尽量在遇到它们时加以指明。书面教程中的
一些示例在这里做了少量改动,这些改动通常只是表面上的,可以忽略。
不必觉得必须完全理解一页上的所有内容才能继续往下——很多时候,随着
你继续前进、能把概念与语言的其他方面联系起来时,它们会变得更加清晰。
Each part of the tutorial defines a set of variables. Some of these are named e1, e2, and so on. These e variables are the ones which are meant for you to evaluate as you go through the tutorial. Of course you may evaluate any other expressions or variables you wish.
教程的每一部分都定义了一组变量。其中一些命名为 e1、e2 等。这些 e
变量就是供你在阅读教程时求值用的。当然,你可以随意求值任何其他
表达式或变量。
The Haskell Report
Haskell 报告
While the report is not itself a tutorial on the Haskell language, it can be an invaluable reference to even a novice user. A very important feature of Haskell is the Prelude. The Prelude is a rather large chunk of Haskell code which is implicitly a part of every Haskell program. Whenever you see functions used which are not defined in the current page, these come from the Prelude. Appendix A of the report lists the entire Prelude; the index has an entry for every function in the Prelude. Looking at the definitions in the Prelude is sometimes necessary to fully understand the programs in this tutorial.
报告虽然不是 Haskell 语言的教程,但即使对初学者也是非常宝贵的参考。
Haskell 的一个极其重要的特性是 Prelude(标准前导库)。Prelude 是一
大段 Haskell 代码,隐式地包含在每个 Haskell 程序中。每当你看到某个
函数在当前页面中没有定义,它就来自 Prelude。报告的附录 A 列出了整个
Prelude;索引中收录了 Prelude 里每个函数的条目。有时必须查看 Prelude
中的定义,才能完全理解本教程中的程序。
Another reason to look at the report is to understand the syntax of Haskell. Appendix B contains the complete syntax for Haskell. The tutorial treats the syntax very informally; the precise details are found only in the report.
查看报告的另一个理由是理解 Haskell 的语法。附录 B 给出了 Haskell 的
完整语法。教程对语法的讲解非常非正式,精确的细节只能在报告中找到。
You are now ready to start the tutorial. Start by reading the Gentle Introduction section 1 then proceed through the online tutorial using :l (if you are using hugs) to advance to the next part. You should read about each topic first before turning to the associated programming example in the online tutorial.
现在你已经准备好开始本教程了。先阅读《Gentle Introduction》第 1 节,
然后用 :l(如果你用 hugs)逐部分进入在线教程。对每个主题,应当先
阅读书面讲解,再看在线教程中配套的编程示例。
Section: 2 Values, Types, and Other Goodies
第 2 节 值、类型与其他好东西
This tutorial is written in literate Haskell. This style requires that all lines containing Haskell code start with >; all other lines are comments and are discarded by the compiler. Appendix C of the report defines the syntax of a literate program. This is the first line of the Haskell program on this page:
本教程以"文学式 Haskell(literate Haskell)"写成。这种格式要求所有
包含 Haskell 代码的行都以 > 开头;其他所有行都是注释,会被编译器
丢弃。报告的附录 C 定义了文学式程序的语法。本页上 Haskell 程序的第一
行是:
Comments at the end of source code lines start with --. We use these throughout the tutorial to place explanatory text in the program.
源码行末尾的注释以 -- 开头。我们在整个教程中都用它来在程序里
放置解释性文字。
All Haskell programs start with a module declaration, as in the previous module Test(Bool) where. This can be ignored for now.
所有 Haskell 程序都以模块声明开头,就像前面的 `module Test(Bool)
where' 一样。这一点现在可以先不管。
We will start by defining some identifiers (variables) using equations. You can print out the value of an identifier by typing the name of the identifier you wish to evaluate. Not all definitions are very interesting to print out - by convention, we will use variables e1, e2, ... to denote values that are interesting to print.
我们先通过方程来定义一些标识符(变量)。输入你想求值的标识符的名字,
就可以打印出它的值。并非所有定义打印出来都很有趣——按照惯例,我们用
变量 e1、e2……来表示那些值得打印的值。
We will start with some constants as well as their associated type. There are two ways to associate a type with a value: a type declaration and an expression type signature. Here is an equation and a type declaration:
我们先从一些常量及其对应的类型开始。把一个类型同一个值关联起来有
两种方式:类型声明和表达式类型签名。下面是一个方程和一个类型声明:
e1 :: Int -- This is a type declaration for the identifier e1
e1 = 5 -- This is an equation defining e1
You can evaluate the expression e1 and watch the system print 5.
你可以求值表达式 e1,看系统打印出 5。
Remember that Hugs evaluates expressions, not definitions. If you type e1 = 5, a definition, you get an error. Definitions have to be placed in the module.
记住,Hugs 求值的是表达式,而不是定义。如果你输入 e1 = 5——一个
定义——就会得到错误。定义必须放在模块里。
The type declaration for e1 is not really necessary but we will try to always provide type declarations for values to help document the program and to ensure that the system infers the same type we do for an expression. If you change the value for e1 to True, the program will no longer compile due to the type mismatch.
e1 的类型声明其实不是必需的,但我们会尽量总是为值提供类型声明,以
帮助文档化程序,并确保系统为表达式推断出的类型与我们自己指定的
一致。如果你把 e1 的值改成 True,程序就会因为类型不匹配而无法编译。
We will briefly mention expression type signatures: these are attached to expressions instead of identifiers. Here are equivalent ways to do the previous definition:
这里简单提一下表达式类型签名:它们附着在表达式上,而不是标识符上。
下面是与前面定义等价的几种写法:
e2 = 5 :: Int
e3 = (2 :: Int) + (3 :: Int)
The :: has very low precedence in expressions and should usually be placed
in parenthesis.
在表达式中 :: 的优先级非常低,通常应该把它放进括号里。
There are two completely separate languages in Haskell: an expression language for values and a type language for type signatures. The type language is used only in the type declarations previously described and declarations of new types, described later. Haskell uses a uniform syntax so that values resemble their type signature as much as possible. However, you must always be aware of the difference between type expressions and value expressions.
Haskell 中包含两个完全独立的语言:面向值的表达式语言,以及面向类型
签名的类型语言。类型语言只用于前面描述的类型声明以及后文要介绍的
新类型声明。Haskell 采用统一的语法,使值与它的类型签名尽可能相似。
但是,你必须时刻分清类型表达式和值表达式的区别。
Here are some of the predefined types Haskell provides: 下面是 Haskell 提供的一些预定义类型:
type Value Syntax Type Syntax
类型 值的写法 类型的写法
Small integers <digits> Int
小整数
e4 :: Int
e4 = 12345
Characters '<character>' Char
字符
e5 :: Char
e5 = 'a'
Strings "chars" String
字符串
e6 :: String
e6 = "abc"
Boolean True, False Bool
布尔值
e7 :: Bool
e7 = True
Floating point <digits.digits> Float
浮点数
e8 :: Float
e8 = 123.456
Homogeneous list [<exp1>,<exp2>,...] [<constituant type>] 同类型列表(所有元素类型相同)
e9 :: [Int]
e9 = [1,2,3]
Tuple (<exp1>,<exp2>,...) (<exp1-type>,<exp2-type>,...) 元组
e10 :: (Char,Int)
e10 = ('b',4)
Functional described later domain type -> range type 函数类型(定义见后文)
inc :: Int -> Int -- a function which takes an Int argument and returns Int
inc x = x + 1 -- test this by evaluating `int 4'
Here's a few leftover examples from section 2:
下面是第 2 节剩下的几个例子:
e11 = inc (inc 3) -- you could also evaluate `inc (inc 3)' directly
Uncomment (by adding removing the --) this next line to see a compile time type error.
把下面这行的 -- 去掉以取消注释,可以看到一个编译期类型错误。
-- e12 = 'a'+'b'
This is a rather odd error message. Essentially says that there is no way to add characters. If there were, it would be by defining an instance in the class Num (this is where + is defined) for the type Char.
这是一个相当奇怪的错误信息。它本质上是在说没有办法对字符做加法。
如果可以的话,就得为类型 Char 在类 Num(+ 就是在那里定义的)中
定义一个实例。