介紹這個主要是由於下文不少代碼寫在源文件中,而後從ghci加載源文件進行測試。app
建立一個文本文件,在其中輸入,並保存爲add.hs文件函數
-- file: add.hs
add x y = x - y
打開ghci,加載剛纔的文件(假設文件目錄爲e:\haskell\add.hs),命令以下測試
ghci> :cd e:\haskell ghci> :load add.hs ghci> add 2 1
表達式的值在須要用到的時候才被計算,好比在一個文本文檔中輸入以下,並保存爲lazy.hsui
-- file: lazy.hs add x y = if left || right then x + y else x -y left = True right = length [1..] > 0
而後加載這個文件,並執行 add 2 1,你會發現結果爲3,而不是一直等待計算結果,說明right的值並無被計算出來。在不少其餘語言中(如C#),邏輯或or被特殊處理,從而在左份量爲真的時候對右份量短路(short-circuits),而haskell中,(||)運算符只是一個普通的函數,並因爲其延遲計算的特性,致使左份量爲真的時候,右份量不被計算出來。固然了,haskell中也能夠寫出短路的邏輯或函數spa
myOr l r = if l then l else r
haskell中還有一個稱做守衛表達式的東東,上面說的myOr函數等價於設計
-- file: myOr.hs myOr l r | l = l myOr l r = r
咱們看take函數的類型code
ghci> :type take
take :: Int -> [a] -> [a]
符號->是右結合的,即,類型說明等價於orm
take :: Int -> ([a] -> [a])
定義一個新的數據類型對象
-- file: Object.hs data ObjectProperty = Object Int String [String] deriving (Show)
這個類型ObjectProperty與三元組(Int, String, [String])的元素相同,然而haskell認爲它們類型不相同。若是要定義一個ObjectProperty類型變量,blog
ghci> let object = Object 0 "i" ["j", "k"] ghci> :type it it :: ObjectProperty
ghci> :type Object
Object :: Int -> String -> [String] -> ObjectProperty
分析: Object是值(數據)構造器,ObjectProperty是對象類型,固然,也能夠將對象類型與值構造器寫成相同的,如
data Object = Object Int String [String]
然而二者僅僅是名稱相同而已。
對於Object的元素分別爲Int,String等類型,還能夠自定義這些類型的同義類型,以下
-- file: Object.hs type ObjectId = Int type ObjectName = String type ObjectFields = [String] data Object = Object ObjectId ObjectName ObjectFields
其實跟C++中的typedef關鍵字相似。
能夠查看值構造器Object的類型(是一個函數)
ghci> :type Object
Object :: ObjectId -> ObjectName -> ObjectFields -> Object
因爲Object類型中元素是匿名的,能夠經過匹配來獲取元素值,例如
ghci> let object = Object 0 "a" ["b", "c"] ghci> let Object x y z = object ghci> x 0 ghci> :type x x :: ObjectId
-- file: Roygbiv.hs data Roygbiv = Red | Orange | Yellow | Green | Blue deriving (Eq, Show)
其中Red、Orange等都爲值構造器
前面簡單介紹過模式匹配。好比想內窺一下Object(前文介紹的自定義代數數據類型)的內部元素,則可使用模式匹配,咱們此次定義一個函數來獲取某個元素
getId (Object id _ _) = id getName (Object _ name _) = name getFields (Object _ _ fields) = fields
然而這種方法仍是顯得代碼大塊,看起來不爽。
能夠定義記錄類型,以下
-- file: Object.hs data Object = Object { oid :: Int, oname :: String, ofields :: [String] } deriving (Show)
這個類型定義其實跟下面的形式幾乎相同
-- file: Object.hs data Object = Object Int String [String] deriving (Show) getId :: Object -> Int getId (Object id _ _) = id getName :: Object -> String getName (Object _ name _) = name getFields :: Object -> [String] getFields (Object _ _ fields) = fields
加載上面那個文件時,依然能夠向之前那麼使用Object類型
ghci> let object = Object 1 "az" ["b"]
或者以下使用
ghci> -- order of elements can be varied
ghci> let object = Object{oid = 10, ofields = ["i"], oname="str"}
跟C#中的泛型相似。好比定義一個類型MyMaybe以下(注意與Prelude模塊中的Maybe區分,雖然功能相似)
-- file: Maybe.hs data MyMaybe a = MyJust a | MyNothing
這裏a就是參數化類型,a能夠是String或者Int等,還能夠是MyMaybe類型,即嵌套
ghci> let a = MyJust 1 ghci> let b = Just a ghci> :type b b :: Num a => Maybe (MyMaybe a)
haskell中的List類型與C#中的不一樣,它是一個遞歸類型,以下
-- file: MyList.hs data MyList a = Cons a (MyList a) | Empty deriving (Show)
這樣設計,使得對其進行遞歸計算方便。
好比獲取List的首項,能夠寫成以下
-- append to the former MyList.hs file end x = Empty y = Cons 1 x myHead (Cons i j) = i myHead Empty = error "MyList.myHead: empty list"
這裏對Empty獲取首項會拋出一個錯誤,這樣處理仍是能夠的,咱們不能返回Empty,不然跟myHead的第一個計算式的返回類型不一樣(第一個計算式返回類型爲a,而Empty類型爲MyList a)。不過,若是結合Maybe類型,或者上面介紹的MyMaybe類型,則能夠避免拋出錯誤,從而使得代碼更好看些。處理以下
myHead (Cons i j) = MyJust i
myHead Empty = MyNothing