learnhaskell/guide-zh_tw.md
2015-04-17 18:45:17 +08:00

29 KiB
Raw Blame History

前言

這是我推薦的學習Haskell之路。

請切記:別在不懂的地方打轉,先繼續讀下去!

社群

IRC頻道是Freenode上的#haskell-beginners

IRC web版用戶端可在這裡取得

Haskell郵件群組

社群參與原則

請參考Chris Done所撰Teaching

請友善待人,尖酸苛薄只會把人嚇跑、令人不願再參與而已。

低劣的批評只讓你自己痛快,對聽者毫無幫助。

別說『這很簡單』、『這沒什麼』。這會讓人覺得要花這麼多功夫來弄懂是因為自不如人,學得慢的人通常是學得最全面的人,這值得稱讚!

當別人承認他不知道的時候,不要故作驚訝。這會讓他難過,而你除了表現得好像很行,什麼也沒得到。

不要說『其實...這樣才對...』(well, actually...)。當有人說了什麼『幾乎正確』的話,而你說些『其實...這樣才對』來做些枝微末節的修正,這很惱人,尤其這常常跟整個討論根本八竿子打不著。我並不是在說人們不在乎精確,只是像這樣的發言通常是作秀成分居多,而非為了追尋真實。


以上部分內容來自the Recurse Center手冊。感謝他們願意公開分享!

什麼是Haskell、GHC和Cabal

Haskell的規格可在下面這篇報告找到此報告最新版本為2010版 onlinereport

GHC

GHC是Haskell語言的主流工具選擇。它包含編譯器、直譯器、套件管理與其他輔助工具。

Cabal

Cabal可用來做專案管理與套件相依性解析。 這會是你用來安裝專案、套件的主要工具,其常見的做法是安裝到專屬的沙箱(cabal sandbox)中。

Cabal相當於Ruby Bundler、Python pip、Node NPM、Maven等等。你可以用GHC來打包套件Cabal則可用來選擇你想要的版本安裝。

環境設定

Ubuntu

這個PPA很棒我在我所有的Linux環境與建置用機器上都靠它。

詳細設定步驟如下:

$ sudo apt-get update
$ sudo apt-get install python-software-properties # v12.04 and below
$ sudo apt-get install software-properties-common # v12.10 and above
$ sudo add-apt-repository -y ppa:hvr/ghc
$ sudo apt-get update
$ sudo apt-get install cabal-install-1.20 ghc-7.8.4 happy-1.19.4 alex-3.1.3

接著,把以下路徑加入你的$PATH環境變數中(bash_profile, zshrc, bashrc, etc)

~/.cabal/bin:/opt/cabal/1.20/bin:/opt/ghc/7.8.4/bin:/opt/happy/1.19.4/bin:/opt/alex/3.1.3/bin

註: 你不妨把.cabal-sandbox/bin加到你的路徑中。如此一來,只要你使用沙箱(cabal sandbox)開發,並且 留在專案的工作路徑中,你就可以在命令列中輕易取用你正在開發的二進位檔。

Debian

使用Ubuntu PPA

如果不打算使用官方提供的穩定版本你可以用上面提過和Ubuntu一樣的流程但會需要在下面這個命令後

sudo add-apt-repository -y ppa:hvr/ghc 加上:

$ sudo sed -i s/jessie/trusty/g /etc/apt/sources.list.d/hvr-ghc-jessie.list

其他的Debian版本只需將jessie都換成你的版本名即可。

如果/etc/apt/sources.list.d/hvr-ghc-jessie.list不存在,那麼/etc/apt/sources.list應該會有:

deb http://ppa.launchpad.net/hvr/ghc/ubuntu jessie main

把上列jessie換成trusty即可。

自行編譯

請參照這篇為Mac OSX所撰的指南

請注意:

  • 根據你個人的工作環境設定ghc時指定目錄前綴(prefix)
  • 不要直接下載cabal-install的二進位檔,請下載源碼並執行其bootstrap.sh腳本。

Fedora 21

從非官方套件庫安裝Haskell 7.8.4 (Fedora 22以上已經有官方版本)

$ sudo yum-config-manager --add-repo \
> https://copr.fedoraproject.org/coprs/petersen/ghc-7.8.4/repo/fedora-21/petersen-ghc-7.8.4-fedora-21.repo 
$ sudo yum install ghc cabal-install

根據petersen/ghc-7.8.4 copr page此版本的ghc 無法與Fedora/EPEL ghc並存。

Arch Linux

從官方套件庫安裝:

$ sudo pacman -S cabal-install ghc happy alex haddock

Gentoo

你可以透過Portage來分別安裝Haskell Platform的各個組件。如果你使用ACCEPT_KEYWORDS=arch,而非ACCEPT_KETWORDS=~arch Portage會弄個老舊的Haskell給你。因此舉凡用了ACCEPT_KEYWORDS=arch,請把下面這幾行加進去:

dev-haskell/cabal-install
dev-lang/ghc

接著請執行:

$ emerge -jav dev-lang/ghc dev-haskell/cabal-install

Gentoo會留一個『穩定』(換言之:老舊)的cabal-install在Portage的套件樹中你可以利用這個cabal-install來安裝 新版的cabal-install。請注意,以下反斜線是必須的:

$ \cabal update                # The backslashes
$ \cabal install cabal-install # are intentional

如此一來你便透過Protage在系統中安裝了cabal又在你的個人環境中安裝了最新的cabal-install。 下一步是確定每次你在終端機執行cabal你的shell都是執行你個人環境中的最新版本

PATH=$PATH:$HOME/.cabal/bin
alias cabal="$HOME/.cabal/bin/cabal"

不知道你的shell是哪一個那你很可能用的是Bash。如果你用的是Bash你需要編輯~/.bashrc。 如果是Z-shell則是~/.zshrc,可用以下面命令來查詢:

echo $SHELL | xargs basename

例如我用的是zsh所以上列命令會輸出zsh

當以上都完成,請再另外安裝兩個工具:alexhappy

$ cabal install alex happy

恭喜你有了一個正常運作的Haskell

Mac OS X

10.9

請安裝GHC for Mac OS X它包含了GHC與Cabal。安裝完成後 它會指示你如何將GHC與Cabal加入你的系統路徑。

10.6-10.8

請下載這個tarball 並安裝其包含的二進位版。

Windows

  • windows minimal GHC installer 它可以用來編譯network等套件雖然嚴格說它還在beta但應該足夠讓任何讀這篇導覽的人使用。

別忘了要用系統管理者的身份來安裝因為它需要新增到Program Files的權限。

其他Linux使用者

下載cabal與ghc的最新版二進位檔。

主要學習課程

Yorgey's cis194課程

請先透過這門課學習這是我最推薦入門Haskell的課。

此課程的教材可於線上取得

Brent Yorgey的課是我目前所知最好的。它之所以好,因為 它不只教你些基礎知識還教你parser combinators。

如果你不是個程式設計師,或缺乏經驗,那麼這門課可能沒這麼適合。建議你從 Thompson的這本書開始然後再轉到cis194。


NICTA課程

在你完成上述Yorgey的cis194後我推薦繼續挑戰此課程。

這門課發佈在github上

透過實作cis194中所介紹過的種種抽象表述你會有更深入的了解。這樣的練習對於 熟悉Haskell中每天都會面對的Functor/Applicative/Monad等等至關重要。 先做cis194緊接著NICTA是這整篇Haskell學習導覽的核心也是我教每個人Haskell的方式。


補充課程 cs240h

提供更多中階以上議題的教材

線上教材

這是Bryan O'Sullivan在Stanford所教課程的線上版。 如果你不知道他是誰去翻翻Haskell的函式庫吧幾乎一半以上常用的套件都有他的名字。 特別是phantom types、information flow control、language extensions、concurrency、pipes和lenses。


<- / do / list comprehension簡便語法到底是什麼

很棒的解釋

了解list和fold

學習常用的typeclasses

對瞭解FunctorApplicativeMonad、`Monoid和其他typeclasses很有幫助而且還有 些針對Haskell的範疇論(category theory)的解釋:

了解基本的Haskell錯誤訊息


Laziness, strictness, guarded recursion

演示

let a = 1 : a -- guarded recursion, (:) is lazy and can be pattern matched.
let (v : _) = a
> v
1
> head a -- head a == v
1

let a = 1 * a -- not guarded, (*) is strict
> a
*** Exception: <<loop>>

IO

glaebhoerl在Reddit討論串的留言

有趣的補充筆記: GHC需要將state token representation隱藏在抽象的IO型別後面 因為state token必須線性地使用不能複製或丟棄但型別系統無法強制這件事。 某個乾淨、lazy、類似Haskell的語言的型別有uniqueness特性(類似linear type但可能有些 我沒意識到的細微差別)為了方便它直接暴露World-passing並提供非抽象的IO monad。

Monads and monad transformers

在你了解typeclasses、Monoid、Functor和Applicative之前請不要做下列練習

嘗試自行實作標準函式庫中的monads(List、Maybe、Cont、Error、Reader、Writer、State),可以讓你 更了解它們。再來不妨嘗試用下述技術實作一個小型expression language的monadic直譯器 Monad Transformers Step by Step(在下列monad transformer章節亦有提及)

透過用不同的monad改變語意從而產生不同的直譯器help convey what's going on。

再來,實作Control.Monad中的函數,例如:mapMsequence是個練習撰寫generic monadic code的好機會。

前面提到過的NICTA課程也可以用來當這個過程的指南它也包括了如何撰寫你自己的Applicative。

Credits:

Monad transformers

Testing, tests, specs, generative/property testing

  • Kazu Yamamoto的這篇教學堪稱典範!

  • Simple-Conduit這個簡單的函式庫對於學習IO串流如何工作很有幫助 所學亦可應用在其他函式庫例如Pipes和Conduit。

Parsing in Haskell

Parsing與產生JSON

Aeson是Haskell標準的JSONparsing解決方案。你可以從hackagegithub取得。

圖學演算法與資料結構

開發環境

Emacs

Vim

Sublime Text

Cabal常見問答

一篇超讚的常見問答

不但對各種主題都有很好的導覽也包含了Cabal的一些重要基礎。

Cabal導覽

在引入沙箱(sandbox)前Cabal地獄(Cabal Hell)對所有Haskell使用者來說都是一大問題。 在沙箱外安裝的套件會直接裝在你的用戶套件資料庫(user pacakge-db)中。除非是常用的基礎套件, 例如Cabal、alex、happy等這絕不是個好方法。除非你很清楚你自己在做什麼任何套件都不該 安裝在用戶資料褲或全域資料庫(global package-db)。

這裏有些如何避免Cabal地獄的最佳指南

如果要實驗新套件,或是起始新專案,在一個新目錄中執行cabal sandbox init

簡言之:

  • 無論是安裝新套件、建置新舊專案、做任何實驗,請用沙箱。
  • cabal repl來啟動project-scoped ghci實體。

我所建議這種以沙箱為基礎的方式應該可以避免套件相依性的問題。但這與Haskell Platform提供 預先編譯套件的方法不相容。如果你還在學習Haskell而且不太了解ghc-pkg和Cabal如何運作 不要用Haskell Platform,改用前面所提的安裝方式。

Stackage

如果你面臨一些建置上的問題(特別是Yesod)不妨考慮用Stackage

據作者所言Stackage通常比cabal freeze更實用。

Hoogle與Haddock

依型別表述搜尋源碼

Hoogle搜尋引擎可依型別搜尋。

比方說,請看以下搜尋(a -> b) -> [a] -> [b]的結果:

搜尋結果.

fpcomplete所管理的在此

另外Hayoo預設開啟了對所有hackage的搜尋。

設定你自己本地端的Hoogle

詳細方法請看這篇文章

Haddock

  1. 修正你的hackage文件 Fix your hackage documentation

  2. Hackage文件第二版 Hackage documentation v2

請注意,以上這些文章都有些過期例如現在Hacakge已支援shiny new info with documentation info and build status.

真正重要的事

為了讓haddocks含入相關套件的文件你必須在~/.cabal/config設立ducumentation: True。如果它被設為False或間接被default(False)關閉你會需要刪除並重新安裝所有套件再產生haddocks。

請記住,因為$pkg參數會被cabal內插html-locationcontent-location參數必須以單引號括入再插入shell命令或包含在shell腳本中。在Makefile中是不行的因為它會被當作Make的變數

#! /usr/bin/env sh

# 如果把反斜線去掉,你可以把它寫成一行
cabal haddock --hoogle --hyperlink-source                       \
 --html-location='http://hackage.haskell.org/package/$pkg/docs' \
 --contents-location='http://hackage.haskell.org/package/$pkg'

TravisCI

如果你跟我一樣,是TravisCI的超級粉絲,那我強力建議你參考multi-ghc-travis為你的Haskell專案的travis.yml設定檔做基礎。

前端/JavaScript

我們的選擇多得驚人!我個人推薦三種:

我用哪一種前端語言?

GHCJS和Haste都是純HaskellGHCJS比Haste能和更多的Haskell套件相容但這不會影響大多數的前端專案。PureScript並非Haskell因此無法直接和你的後端分享源碼。

GHCJS的執行期payload是最大的大約100kb (luite正在研究如何解決此問題)Haste則和PureScript差不多。

PureScript有最好的JS工具鏈整合(用gulp/grunt/bower)GHCJS和Haste則與Haskell工具鏈整合較佳(例如Cabal)。

以上三者都是極佳選擇,大多數的前端專案都適用。

想要更充分了解laziness、NF、WHNF

關於lazy lambda calculus的研究論文

平行/並行(Parallelism/Concurrency)

Lenses and Prisms

在你習慣Haskell後我強烈建議你學習Lenses與Prisms。你不必了解底層的原理只要當一個使用者就很受用。

大家普遍誤會Lens是個很難用的東西其實任何一個了解Functor/Foldable/Traversable甚至只知道Functor的人都可以運用Lenses與Prisms來讓他們的開發生涯更快樂。

如果你曾經做過:(fmap . fmap)你其實已經有Lense的思維了。

我推薦以下兩篇教學:

詳細資料請看這裡:Lens package on hackage.

遞迴範式 (Recursion Schemes)

你一定聽過些瘋狂的『*-morphism』他們其實只是遞迴。在嘗試搞懂前你應該要先知道如何實作list至少一種其他資料結構的foldr例如tree (folds叫做catamorphisms)。再進一步瞭解如何在以上資料結構實作unfold (anamorphism)會讓整體知識完善些。

以下資料與traversable和foldable的概念相合。

GHC核心與效能調校

型別(Type)與範疇論(Category Theory)

寫Haskell不用學,僅供有興趣的人參考!

如果你想開始學習型別與範疇論:

書籍

Stephen俏皮的"How to get to monad"文章

其他有趣的主題

Parametricity, ad-hoc vs. parametric polymorphism, free theorems

Initial與Final、DSLs、與Tagless

Comonads

Yoneda / CoYoneda

Propositions vs. Judgments (computation)

Dependent typing

靜態連結二元檔Statically linking binaries

補充資料

有部分已在本文提及

對話記錄

本儲存庫中

裡面有些非常重要而有幫助的資訊,可協助你深入了解許多不同的議題。