這只是回答了一部分:
如果我想[...]改變我的數據類型,我不得不改變所有的功能。
在這種情況下,你能避免受 定義與pattern synonyms extension一個自定義模式改變所有的功能:
{-# LANGUAGE PatternSynonyms #-}
-- The old type:
-- data Tree = Node Int Tree Tree
-- | Leaf
-- The new type
data Tree = NewNode Tree Int Tree -- changed name & argument order
| Leaf
pattern Node n left right = NewNode left n right
add :: Tree -> Int -> Tree
add (Node n left right) x = Node (n+x) (add left x) (add right x)
add (Leaf) x = Leaf
-- etc.
上面,我改名爲舊構造Node
爲NewNode
,也改變了順序的論點。然後,我將pattern Node
定義爲兼容性橋樑,使舊模式再次起作用。
問題也問:
有沒有辦法再使用這些模式,所以我只需要定義一次?
@PyRulez評論使用記錄通配符縮短模式。這裏有一個可能的方法:
{-# LANGUAGE ViewPatterns, RecordWildCards #-}
-- an auxiliary record type to define the custom pattern
data R = RNode { left::Tree , n::Int, right::Tree } | RLeaf
-- a function to convert a Tree to a R
toR :: Tree -> R
toR (NewNode l n r) = RNode l n r
toR _ = RLeaf
-- Here we use a shorter pattern:
add2 :: Tree -> Int -> Tree
add2 (toR -> RNode{..}) x = Node (n+x) (add2 left x) (add2 right x)
-- ^^^^^^^^^^^^^^^^^^^ this brings in scope left, n, right
add2 (toR -> RLeaf) x = Leaf
在這種特殊情況下,空間沒有太大的增益。但是,無論模式有多大,在記錄定義(和輔助功能)之後,我們只需編寫toR -> RNode{...}
。
雖然我不確定我是否真的喜歡這個。
首先,該記錄污染全局名稱空間,具有三個(部分!)函數left :: R -> Tree, n :: R -> Int, right :: R -> Tree
。這會引發一些警告,如果您嘗試使用例如n
作爲另一個無關函數的參數(The local name shadows the global name n
)。其次,記錄通配符擴展引入了一些變量,這些變量不是寫在代碼中的 - 讀者必須知道(或猜測)這些變量是什麼。另外請注意,RNode{..}
模式帶來n
納入與全球名稱不同的類型:Int
而不是R -> Int
。讀者可能認爲n
是全球性的,即使在類型層面也會被誤導。
三,上面的代碼使用視圖模式,目前這些混淆全面性檢查:
Patterns.hs:23:1: Warning:
Pattern match(es) are non-exhaustive
In an equation for ‘add2’: Patterns not matched: _ _
在這種特定的情況下,我們可以簡單地寫
add2 :: Tree -> Int -> Tree
add2 (node -> RNode{..}) x = Node (n+x) (add2 left x) (add2 right x)
add2 _ x = Leaf
避免了警告,但一般來說,我們並不總是有這種選擇。
來源
2015-07-03 22:30:07
chi
如果您使用GHC,則可以使用[記錄通配符](http://downloads.haskell.org/~ghc/latest/docs/html/users_guide/syntax-extns.html#record-wildcards)。 – PyRulez