Write Yourself a Scheme in 48 Hours/First Steps

原文。
https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours/First_Steps

首先,你需要安装一个ghc。在Linux上,它常常被预先安装好了或者能够通过apt-get或者yum命令来轻松搞定。你也可以从官网直接下载它。不过除非你确信你想从源码去编译它,否则下载一个二进制包就可以了。像安装其他的软件包一样下载和安装它既可。这个教程是在Linux下完成的,但是只要你会使用相关的命令行操作,它在Windows或是Mac环境下也一样能工作。

对UNIX或者Windows Emacs用户来说,这里有一个很棒的Emacs mode,包括了语法高亮和自动缩进的功能。Windows用户则能够直接使用记事本或者其他文本编辑器:Haskell的语法对记事本相当友好,尽管你仍要小心处理缩进。Eclipse用户建议使用eclipsefp插件。

现在,是时候写你的第一个Haskell程序了。这个程序将通过命令行读入一个名字然后输出一个问候语句。建立一个以 ".hs” 结尾的文件并输入下列代码。当心缩进,否则你的程序可能没法通过编译。

module Main where  
import System.Environment  

main :: IO ()  
main = do 
      args <- getArgs  
      putStrLn ("Hello, " ++ args !! 0)  

我们来看下这段代码。前两行表示我们讲创建一个名叫Main的模块,并且导入了System这个模块。所有的Haskell程序都会从Main模块里的一个叫做main函数的地方开始运行。你可以在这个模块中导入其他的模块,但是如果没有了它,编译器就无法生成可执行文件供用户运行。此外Haskell是大小写的敏感的:模块名称需要是大写开头的,而函数声明则必须是非大写开头的。

main :: IO ()这行是函数的类型声明:它表示main函数的类型是IO(),是一个返回Unit类型()的IO操作。一个Unit类型仅会包含一个值,而(),它表示什么也没有。类型声明在Haskell里是可选的:编译器能够自动的识别它们,当你的声明和编译器自动识别发生冲突的时候它则会报错。在这个教程中,为了更清晰的说明,所有的类型都是显式声明的。而你在家里跟着做的时候,你可能更愿意选择忽略,因为在编写这个程序的时候其实并不太需要去在意它们。

IO类型是Monad类型类的一个实例,Monad是一种抽象的概念,如果满足以下这两个条件,那我们就会说这样的值是Monad:

  1. 这个值包含了一些特定类型的附加信息;
  2. 大多数函数不需要去关心这些附加的信息。

在这个例子里,

  1. 这个附加的信息就是将被执行的IO操作;
  2. 而这个附加信息的值是不存在的,表示成()

IO[String]IO()都同样属于IO Monad类型,但它们有着不同的基本类型。它们作用于(或是传递)不同类型的值,[String]()

那些包含了附加信息的值则被称作“Monadic”。

Monadic值常被称作“操作” ,因为最容易的思考IO Monad用途的方法就是把它当做一系列可能会影响外界世界的动作。这一系列动作会传递一些基础的数值,然后在这个过程中每个动作都会对这些值进行影响。

Haskell是一个函数式的语言:与给出计算机一系列指令从而让它执行不同,你需要给Haskell一系列定义来告诉它每一个函数来如何处理。这些定义会将各种动作和函数组合在一起。而编译器会识别出将它们组织在一起的执行方式。

要写出这样一个定义,你首先需要建立一个等式。等式的左边是一个名称,可能还会带有若干个与变量绑定的模式(后面会解释)。右边的话,会给出一些由其他定义组合而成的式子,从而告诉计算机如何遇到该定义时如何进行计算。这些等式就和一般的代数表达式一样:你总是可以在程序中用等式右边的部分来替代左边的名字,并且得到与之前相同的结果。这种行为被称作“引用透明”,而这种性质使得Haskell代码比其它的语言更加易于理解。

那我们应该怎么定义我们的main函数呢?我们知道它必须是一个能够从命令行读入参数,然后从打印出一些输出,最终返回()(空值)的IO()操作。

这里有两种方法创建一个IO操作:

  1. 使用return函数提升一个普通值进入IO Monad。
  2. 连接两个已经存在的IO操作。

因为我们接下来要做两件事情,所以我们选择第二种方法。我们通过内建函数getArgs读入命令行参数并把它们存入一个字符串列表。而内建函数putStrLn则能够读入一个字符串然后将它输出到终端。

我们使用一个do代码块来连接这两个操作。一个do代码块包括很多行,所有的行按照第一个非空白字符在do后面排列,并且每行都可能是如下两种形式之一:

  1. name <- action1
  2. action2

第一种形式将action1的结果和name绑定,从而你可以在下一个操作中使用它。例如,如果有action1的类型是IO[String](一个会返回一个字符串列表的IO操作,就和getArgs一样),那name就会在接下来的一系列操作里和这个返回的字符串列表通过绑定操作符>>=绑定在一起。第二种情况仅仅执行这个action2,并通过>>操作符同下一行连结在一起。绑定操作符在处理不同Monad的情况下有不同的语义:在IO Monad中,它会连续执行所有的操作,然后对外部世界产生这些操作带来的副作用。由于这个绑定符号的语义依赖你具体使用的Monad类型,所以你并不能在同一个do代码块里把不同类型的Monad类型的操作糅杂在一起---在这里只有IO Monad是可用的(在同一个管道中)。

当然,这些操作可能自己会调用其他函数或是复杂的表达式,然后继续传递它们的计算结果(通过调用return或是其他最终调用了return的函数)。

在这个例子里,我们首先取出参数列表中的第一个元素(args !! 0),然后把它拼接到字符串"Hello,"的后面("Hello," ++),最后把结果传给putStrLn。

就这样,一个包含了之前所说的读取和打印操作的新的操作就这样创建完毕并存到了main这个返回值为IO()的标识符中。这样Haskell系统就能够识别并运行它了。

Haskell中,字符串即是字符的列表形式,所以你可以对它使用任何的
列表函数或是操作符。以下是一个完整的标准操作符列表和它们对应的优先级:


懒得作图就直接截图的操作符一览表

接下来编译和运行这个程序:

debian:/home/jdtang/haskell_tutorial/code# ghc -o hello_you --make listing2.hs
debian:/home/jdtang/haskell_tutorial/code# ./hello_you Jonathan
Hello, Jonathan

-o 选项指定了你想编译出来的可执行文件的路径,然后你需要指定Haskell源代码的路径。

习题

  1. 修改程序,让它能够从命令行读取两个参数然后打印出一条包含它们的信息。
  2. 修改程序,让它能够使用输入的参数进行简单的四则运算,建议使用read来讲字符串转化成数字类型,并用show来进行相反的操作。对各种不同的动作都操练一番。
  3. getLine是一个从命令行读取一行输入信息然后返回字符串的IO操作。修改程序,让它能够提示需要一个名字并读取这个名字而不是像之前那样直接从命令行传入参数,最后打印它。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容