29 KiB
前言
這是我推薦的學習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
。
當以上都完成,請再另外安裝兩個工具:alex
和happy
:
$ 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。
Data61課程
在你完成上述Yorgey的cis194後,我推薦繼續挑戰此課程。
這門課發佈在github上。
透過實作cis194中所介紹過的種種抽象表述,你會有更深入的了解。這樣的練習對於 熟悉Haskell中每天都會面對的Functor/Applicative/Monad等等至關重要。 先做cis194,緊接著Data61,是這整篇Haskell學習導覽的核心,也是我教每個人Haskell的方式。
補充課程 cs240h
提供更多中階以上議題的教材
這是Bryan O'Sullivan在Stanford所教課程的線上版。 如果你不知道他是誰,去翻翻Haskell的函式庫吧!幾乎一半以上常用的套件都有他的名字。 特別是phantom types、information flow control、language extensions、concurrency、pipes和lenses。
<-
/ do
/ list comprehension簡便語法到底是什麼?
了解list和fold
學習常用的typeclasses
對瞭解Functor
、Applicative
、Monad
、`Monoid`和其他typeclasses很有幫助,而且還有
些針對Haskell的範疇論(category theory)的解釋:
了解基本的Haskell錯誤訊息
Laziness, strictness, guarded recursion
-
Marlow關於平行與同步的書中,關於laziness與normal form的介紹 是我所看過最好的。如果沒有立即理解,不妨搭配以下補充材料。
-
SO上的討論串'Does haskell have laziness?'
-
Johan Tibell'在reasoning about laziness這個演講的投影片
演示
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
-
Haddocks for System.IO.Unsafe.unsafePerformIO 請讀unsafeDupablePerformIO的文件和實作筆記。
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
中的函數,例如:mapM
或sequence
是個練習撰寫generic monadic code的好機會。
前面提到過的Data61課程也可以用來當這個過程的指南,它也包括了如何撰寫你自己的Applicative。
Credits:
Monad transformers
Testing, tests, specs, generative/property testing
-
Kazu Yamamoto的這篇教學堪稱典範!
-
Simple-Conduit:這個簡單的函式庫對於學習IO串流如何工作很有幫助, 所學亦可應用在其他函式庫,例如Pipes和Conduit。
Parsing in Haskell
-
Parser combinator tutorial for Haskell using Parsec
Parsing與產生JSON
Aeson是Haskell標準的JSONparsing解決方案。你可以從hackage或github取得。
圖學演算法與資料結構
開發環境
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
-
修正你的hackage文件 Fix your hackage documentation
-
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-location
和content-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
我們的選擇多得驚人!我個人推薦三種:
-
Haste Haskell至JavaScript的編譯器。a Haskell to JavaScript compiler
-
- GHCJS簡介 GHCJS Introduction
- Functional Reactive Web Interfaces with GHCJS and Sodium
- 用Haskell搭配ghcjs撰寫Atom插件 Writing Atom plugins in Haskell using ghcjs
我用哪一種前端語言?
GHCJS和Haste都是純Haskell,GHCJS比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)
-
Parallel and Concurrent Programming in Haskell。在我所讀過的文獻中,Simon Marlow所撰的這本書是此主題的佼佼者。
-
這篇教學帶領你一步步學習如何用Haskell測試、漸進開發多緒應用程式。
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的概念相合。
-
Don't fear the cat - Good demonstration of how hylomorphism is the composition of cata and ana.
-
Recursion Schemes - This field guide is excellent.
-
Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire
GHC核心與效能調校
型別(Type)與範疇論(Category Theory)
寫Haskell不用學,僅供有興趣的人參考!
如果你想開始學習型別與範疇論:
-
haskell wikibook 有不錯的圖解。
-
Haskellwiki上的Category Theory也有不錯的參考資料。
-
Pierce的Great Works in PL列表。
書籍
-
Kmett推薦:Quora Question: What is the best textbook for category theory?
-
Harper's Practical Foundations for Programming Languages是我讀過以程式語言角度切入的最佳介紹。
Stephen俏皮的"How to get to monad"文章
其他有趣的主題
Parametricity, ad-hoc vs. parametric polymorphism, free theorems
Initial與Final、DSLs、與Tagless
-
The dog that didn't bark較無相關,但非常有趣。
Comonads
Yoneda / CoYoneda
-
Free monads for Less, Edward Kmett的系列文章
Propositions vs. Judgments (computation)
Dependent typing
靜態連結二元檔Statically linking binaries
補充資料
有部分已在本文提及
對話記錄
在本儲存庫中。
裡面有些非常重要而有幫助的資訊,可協助你深入了解許多不同的議題。