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閱讀 227,488評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370

推薦閱讀更多精彩內容