Haskell 編程入門

在過去的幾個月裏,學習Haskell讓我以爲很是快樂,可是入門的過程並無我原先想象的那麼簡單。我很是幸運地在一個正確的地方工做,而且所以可以在Facebook參加Bryan O'Sullivan的Haskell課程。在Try Haskell上玩了一段時間後,最終你就會想要在本身的電腦上安裝GHC了。html

haskell platform mac os x

設置Cabal

Cabal是Haskell用於構建應用程序和庫的公共架構。和其配套使用的工具包是Hackage,做用則相似於Perl的CPAN,Python的pip或者Ruby的gem。你可能對其表示失望,可是它也不至於糟糕。node

當最終安裝完cabal包時,默認會把它安裝到 ~/.cabal/目錄,腳本則安裝到 ~/.cabal/bin目錄下。緊接着,還要添加環境路徑。如下設置便可知足,但也能夠按照你本身喜愛來設置shell的profile: ios

echo 'export PATH=$HOME/.cabal/bin:$PATH'>> ~/.bashrc

在使用cabal工做以前,你還須要更新可用包列表。這只是偶爾是使用到而已,特別在安裝或者更新新的包時。git

cabal update

這時,~/.cabal/config 文件的library-profiling 選項還沒開啓。若是你如今不開啓,之後再開啓則可能須要從新編譯全部的文件。開啓此選項,須要編輯~/.cabal/config文件,而且設置--library-profiling: Falsetolibrary-profiling: True。github

$forfin ~/.cabal/config;
do
  cp $f $f.old && sed-E's/(-- )?(library-profiling: )False/\2True/'< $f.old > $f;
done

在安裝其餘東西前,還須要安裝 Calbal installer:shell

cabal install cabal-install

安裝ghc-mod(更好的Emacs/Vim的支持)

ghc-mod是在Emacs和Vim中集成GHC的環境.你也許可以從SublimeHaskell中使用Sublime
Text2和ghc-mod.我至今只試過在Emacs中使用過它.Vim用戶能夠嘗試hdevtools,它編譯更加迅速而且一樣準確(參考kamatsu's comment).編程

$ cabal install ghc-mod

顯然你必須爲你的Emacs配置它,而且我會將我如今的 ~/.emacs.d留給你作參考.segmentfault

安裝 Cabal-dev(構建沙盒的工具)

Cabal-dev是一個能幫助你安裝Haskell軟件的工具。它相似於Python 的 virtualenv或者Ruby的rvm,可是使用上有些不一樣。Cabal-dev 能讓你免於「Calbal Hell」(即你如今安裝的包和你以前安裝的包存在依賴衝突)的煩惱。bash

儘可能使用calbal-dev 代替僅僅使用cabal進行編譯工做。其主要代價是花費多一點時間編譯那些已經安裝在某處的包(和浪費一些磁盤空間),不過這也是至關划算的了。markdown

使用源碼編譯和安裝Cabal-dev:

若是你想嘗試一些工具,可是並不想污染你的haskell環境,你可使用cabal-dev。cabal-dev默認的沙盒在 ./cabal-dev,可是你能夠把它放到任何地方。下面的例子中, 我將安裝 darcs 2.8.2 (一個haskell寫的版本控制系統)到/usr/local/Cellar/darcs/2.8.2中,並使用Homebrew建立符號連接。在其餘平臺你可能但願使用本身指定的路徑和PATH環境變量。

$ cabal-dev install -s /usr/local/Cellar/darcs/2.8.2 darcs-2.8.2
$ brew link --overwrite darcs

OK!如今darcs已經在你的PATH中了,並且你並不須要擔憂版本衝突。好吧,你沒辦法徹底擺脫它們,可是沒有之前那麼多了。特別注意,cabal-dev會將全部包安裝在沙盒頂層目錄。這意味着若是兩個包有着相同的依賴關係(徹底相同),它們會互相破壞它們內部指向依賴的包內的文件的符號連接,例如發佈協議文件和文檔。這樣使用--overwrite可能沒有什麼損失,可是你最好先用--overwrite --dry-run選項檢查一番。有些麻煩,可是不會毀掉你一成天的成果。

若是你想看看你所安裝的darcs的版本,使用cabal info darcs。

其餘有意思的haskell寫的工具(不分前後):

  • pandoc - 一個像瑞士軍刀同樣的標記語言轉換器(例如,markdown, reStructuredText, org-mode, LaTeX)
  • gitit - 使用git, darcs或者mercurical做倉庫的wiki
  • pronk - 一個HTTP負載測試工具,像ab或者httperf,只是相比更現代和易用

配置GHCi

GHCi是GHC交互式解釋器的縮寫。更詳細的文檔請參考GHC用戶指南第二章:使用GHCi)。你將在編碼過程當中花上很長一段時間,你大概想要設置一個更短的提示符。它剛開始的時候看起來像這樣:

Prelude>

一旦你開始引入模塊,提示符將不斷變長。真的,你一輩子都不須要這麼長的提示符。

Prelude>:m+Data.List
Prelude Data.List>:m+Data.Maybe
Prelude Data.List Data.Maybe>

配置文件是.ghci。我用一個很是簡單的ASC II提示符,而另外一些人喜歡把提示符設置成諸如"λ>"這樣。

echo':set prompt "h> "'>> ~/.ghci

Hello World:

$ ghci
h>putStrLn"Hello World!"
Hello World!
h>

官方對Hackage支持還很不足,但其也有非官方鏡像.

使人遺憾的是,目前Hackage還不是很穩定。我不知道什麼緣由形成的,但但願他們能儘早作點什麼。Hackage有其工做區,你可使用hdiff.luite.com或者hackage.csc.stanford.edu的倉庫。

修改 ~/.cabal/config 文件行爲:

remote-repo: hackage.haskell.org: http://hackage.haskell.org/packages/archive

變成這樣:

-- TODO When hackage is back up, set back to hackage.haskell.org!
-- remote-repo: hackage.haskell.org: http://hackage.haskell.org/packages/archive
remote-repo: hdiff.luite.com: http://hdiff.luite.com/packages/archive
-- remote-repo: hackage.csc.stanford.edu: http://hackage.scs.stanford.edu/packages/archive

當你完成修改遠程倉庫的設置後,你將須要更新包列表:

cabal update

不要忘記後續把它還原回來!

開始一個項目(使用cabal-dev)

你最終會明白如何去作,可是一個快速的方法是直接使用cabal-dev。下面告訴你開始一個簡單的程序。

對於你本身的項目,你可能想要去掉-n標誌,讓cabal-dev詢問你你想要的選項。-n會使用全部默認的選項而沒有提示。

$ mkdir -p ~/src/hs-hello-world
$ cd ~/src/hs-hello-world
$ touch LICENSE
$ cabal init -n --is-executable

這回生成一個Setup.hs文件和hs-hello-world.cabal文件。下面須要修改main-is這行來講明你要從哪一個文件編譯你的可執行文件。最終的文件可能像這樣。

hs-hello-world.cabal

-- Initial hs-hello-world.cabal generated by cabal init.  For further
-- documentation, see http://haskell.org/cabal/users-guide/

name:                hs-hello-world
version:             0.1.0.0
-- synopsis:           
-- description:        
license:             AllRightsReserved
license-file:        LICENSE
-- author:             
-- maintainer:         
-- copyright:          
-- category:           
build-type:          Simple
cabal-version:       >=1.8

executable hs-hello-world
  main-is:             HelloWorld.hs
  -- other-modules:      
  build-depends:       base ==4.5.*

而後建立一個HelloWorld.hs,可能像這樣:

HelloWorld.hs

main :: IO()
main = putStrLn "Hello, world!"

你能夠編譯並安裝它到一個本地的沙盒:

$ cabal-dev install
Resolving dependencies...
Configuring hs-hello-world-0.1.0.0...
Building hs-hello-world-0.1.0.0...
Preprocessing executable'hs-hello-world'forhs-hello-world-0.1.0.0...
Installing executable(s)in/Users/bob/src/hs-hello-world/cabal-dev//bin
Installed hs-hello-world-0.1.0.0
$ ./cabal-dev/bin/hs-hello-world
Hello, world!

生成的文件比較大,由於他是靜態鏈接的。你能夠複製它到任何處理器架構和操做系統相同的機器上,它能夠直接運行。

你能夠跳過安裝步驟,這樣可能節省一點時間:

$ cabal-dev configure
Resolving dependencies...
Configuring hs-hello-world-0.1.0.0...
$ cabal-dev build
Building hs-hello-world-0.1.0.0...
Preprocessing executable'hs-hello-world'forhs-hello-world-0.1.0.0...
[1 of 1] Compiling Main             ( HelloWorld.hs, dist/build/hs-hello-world/hs-hello-world-tmp/Main.o )
Linking dist/build/hs-hello-world/hs-hello-world...
$ ./dist/build/hs-hello-world/hs-hello-world
Hello, world!

既然這個項目沒有什麼依賴,你可跳過一些步驟。

能夠解釋執行它,不須要編譯:

$ runghc HelloWorld.hs
Hello, world!
$ ghci
GHCi, version7.4.2: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim...linking...done.
Loading package integer-gmp...linking...done.
Loading package base...linking...done.
Prelude>:load HelloWorld
[1of1] Compiling Main             ( HelloWorld.hs, interpreted )
Ok, modules loaded: Main.
*Main>main
Hello, world!

你甚至不須要用cabal-dev(cabal)編譯它:

$ runghc Setup.hs configure
Configuring hs-hello-world-0.1.0.0...
$ runghc Setup.hs build
Building hs-hello-world-0.1.0.0...
Preprocessing executable'hs-hello-world'forhs-hello-world-0.1.0.0...
[1 of 1] Compiling Main             ( HelloWorld.hs, dist/build/hs-hello-world/hs-hello-world-tmp/Main.o )
Linking dist/build/hs-hello-world/hs-hello-world...

可是對一些複雜的項目,你可使用cabal-dev ghci(在cabal-dev configure &&
cabal-dev build以後)。注意它會自動裝載你的可執行文件到解釋器裏:

$ cabal-dev ghci

on the commandline:
    Warning:-O conflicts with--interactive;-O ignored.
GHCi, version7.4.2: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim...linking...done.
Loading package integer-gmp...linking...done.
Loading package base...linking...done.
Ok, modules loaded: Main.
h> main
Hello, world!

GHCi基本使用

更多你想知道的GHCi用法能夠在Chapter 2. Using GHCi找到。

****:t 顯示一個表達式的類型簽名

λ >> :t main
main :: IO ()
λ >> :t map
map :: (a -> b) -> [a] -> [b]
λ >> :t map (+1)
map (+1) :: Num b => [b] -> [b]

:i 顯示一個符號的信息(函數,typeclass,類型等等)

λ >> :i Num
class Num a where
  (+) :: a -> a -> a
  (*) :: a -> a -> a
  (-) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a
    -- Defined in `GHC.Num'
instance Num Integer -- Defined in `GHC.Num'
instance Num Int -- Defined in `GHC.Num'
instance Num Float -- Defined in `GHC.Float'
instance Num Double -- Defined in `GHC.Float'
λ >> :info map 
map :: (a -> b) -> [a] -> [b]  -- Defined in `GHC.Base'
λ >> :info Int
data Int = GHC.Types.I# GHC.Prim.Int#   -- Defined in `GHC.Types'
instance Bounded Int -- Defined in `GHC.Enum'
instance Enum Int -- Defined in `GHC.Enum'
instance Eq Int -- Defined in `GHC.Classes'
instance Integral Int -- Defined in `GHC.Real'
instance Num Int -- Defined in `GHC.Num'
instance Ord Int -- Defined in `GHC.Classes'
instance Read Int -- Defined in `GHC.Read'
instance Real Int -- Defined in `GHC.Real'
instance Show Int -- Defined in `GHC.Show'

:m 增長一個模塊

λ >> :m +Data.List
λ >> sort [10, 9..1]
[1,2,3,4,5,6,7,8,9,10]

:l 導入一個模塊,:r 從新導入模塊

λ >> :! echo 'hello = print "hello"' > Hello.hs
λ >> :l Hello
[1 of 1] Compiling Main             ( Hello.hs, interpreted )
Ok, modules loaded: Main.
λ >> hello 
"hello"
λ >> :! echo 'hello = print "HELLO"' > Hello.hs 
λ >> :r
[1 of 1] Compiling Main             ( Hello.hs, interpreted )
Ok, modules loaded: Main.
λ >> hello 
"HELLO"

推薦的資料

我學習Haskell時發現有些資料很是有用

  • Haskell趣學指南

    我發現這本書是一個很好的起點,我推薦你先讀完它。他並非很是深刻讓你了徹底解GHC如何工做,可是我發現看完這本書以後讀寫Haskell代碼會比較適應。

    Learn you a Haskell for Great Good!

  • 真實世界的Haskell

    這本書比較權威,可是仍然對初學者比較友好。他會教你比較真實的Haskell開發,例如寫測試,配置,IO及併發等等。

    Real world Haskell

站點

IRC

  • 在freenode上的 #haskell 頻道你能夠在任什麼時候間找到幾百個對Haskell感興趣的人。求助的好地方。

建議?

我把我所說的基於建議。若是我漏掉了什麼重要的,請告訴我!我沒有試圖介紹一切,我認爲haskell wiki在這方面作得比我好。這些就是要注意的了。


原文 Getting Started with Haskell
翻譯 crab2313, suzhaoda, ShaoFantasy, 梁洛克, HeliosM
修訂 SegmentFault

相關文章
相關標籤/搜索