拉斐爾的回答是完全正確的,但我想添加一些我認爲相關的觀點。
首先,更概念的東西。調用orange
和banana
變量並不完全正確。相反,它們是常量函數或僅僅是String
類型的常量。這是因爲你實際上不能改變他們的價值觀(因此,我們稱之爲不變性)。關於你的需求問題,我不確定,但可能這個例子的想法很簡單,就是定義兩個不同的「成果」,這樣人們就可以在GHCi(或其他任何地方)通過apple
和whichFruit
以及orange
而不是字符串文字「apple」和「orange」。這就是說,儘管是一個簡單的例子來說明非常基本的功能,但值得一提的是,不建議(函數)定義函數whichFruit
的方式。此功能僅適用於兩種不同的輸入:「蘋果」和「橙色」。除此之外的任何內容都會導致whichFruit
意外失敗。由於whichFruit
僅爲其域的子集(String
)定義,因此稱爲部分函數。換句話說,類型簽名告訴我們whichFruit
是一個函數,期望String
並返回一個Fruit
,但是當它被稱爲「香蕉」時,它會給出錯誤。想象一下,有人導入你的模塊(它定義了whichFruit
)並開始使用它。在瀏覽GHCi的功能時,他發現whichFruit
並使用:t whichFruit
檢查其類型簽名。這很可能是他會想到whichFruit
與任何字符串工作,但是當他打電話whichFruit 「banana」
:
*** Exception: BogusPattern.hs:(11,16)-(13,34): Non-exhaustive patterns in case
這是非常糟糕的,對不對?通過直面的是,人們可能會想到這個問題只與更加明確什麼該函數需要作爲參數,並試圖只是改變代碼看起來像:
type FruitName = String
whichFruit :: FruitName -> Fruit
whichFruit f = case f of
"apple" -> Apple
"orange" -> Orange
雖然使用一種類型的同義詞肯定會增加代碼的可讀性,它肯定不能解決問題,一旦沒有任何東西阻止調用者傳遞whichFruit
傳遞類似「foo」的東西。正因爲如此,在Haskell中,我們努力編寫總計(爲其域的所有輸入定義)的功能,從而產生更健壯的代碼。通過簡單的設置標誌-Wall
在GHCI和加載模塊,我們可以看到,即使編譯器警告我們有關的缺陷:
λ: :set -Wall
λ: :l BogusPattern
[1 of 1] Compiling BogusPattern (BogusPattern.hs, interpreted)
...
BogusPattern.hs:11:16: Warning:
Pattern match(es) are non-exhaustive
In a case alternative:
Patterns not matched:
[]
(GHC.Types.C# #x) : _ with #x `notElem` ['a', 'o']
[GHC.Types.C# 'a']
(GHC.Types.C# 'a') : ((GHC.Types.C# #x) : _)
with
#x `notElem` ['p']
...
Ok, modules loaded: BogusPattern.
好......那麼,我們如何解決這個問題?比較簡單的方法是添加默認情況下:
whichFruit' :: FruitName -> Fruit
whichFruit' f = case f of
"apple" -> Apple
_ -> Orange
雖然它的工作原理,其結果可能是不希望(我們當然不希望whichFruit' "banana"
返回Orange
)。作爲一種替代解決方案,人們可以以(MIS)添加一個新的價值構造的Fruit
數據類型來表示一個無效的水果和返回,在默認情況下(_
):
data Fruit = Apple | Orange | InvalidFruit
deriving (Show)
該解決方案是不是好原因有很多。從語義上講,我們預計值構造函數可以構建與相應的類型構造函數的類型相匹配的值。在我們的例子中,我們預計Apple
和Orange
爲Fruit
s(這很有意義),但InvalidFruit
應該表示什麼意思?此外,事實證明,在Haskell中,我們有更好的方式來表示失敗的可能性,也就是說,爲了將失敗的概念嵌入到whichFruit'
中,我們可以簡單地使用例如Maybe
類型來重寫它,如下所示:
whichFruit'' :: FruitName -> Maybe Fruit
whichFruit'' f = case f of
"apple" -> Just Apple
"orange" -> Just Orange
_ -> Nothing
這個解決方案看起來好多了,在我看來,它是最優的。唯一的缺點是它增加了「額外的開銷」並使調用者函數有點複雜化,調用者函數必須處理帶有可能失敗的附加上下文(Maybe Fruit
)而不是僅值(Fruit
)的值。作爲最後一點,我告訴你,使用這樣的解決方案(或相關的解決方案,即:Either String Fruit
)是完全值得的,而且隨着你對Haskell更有經驗,處理這些「更復雜」的類型變得非常自然,你不會甚至通知。
你打電話給'whichFruit'? – 2014-09-01 04:40:54