này chỉ trả lời cho phần:
Nếu tôi muốn [...] thay đổi kiểu dữ liệu của tôi, tôi phải thay đổi tất cả các chức năng.
Trong trường hợp đó, bạn có thể tránh thay đổi tất cả các chức năng của xác định một mô hình tùy chỉnh với 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.
Ở trên, tôi đổi tên các nhà xây dựng cũ Node
vào NewNode
, cũng thay đổi thứ tự các đối số của nó. Sau đó, tôi đã xác định một cầu nối tương thích pattern Node
, làm cho các mẫu cũ bên dưới hoạt động trở lại.
Câu hỏi đặt ra cũng hỏi:
Có cách nào để tái sử dụng những mô hình, vì vậy tôi chỉ phải xác định chúng một lần?
@PyRulez đã nhận xét về cách sử dụng ký tự đại diện để rút ngắn mẫu. Dưới đây là một cách có thể:
{-# 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
Trong trường hợp cụ thể này, không có sự gia tăng lớn về không gian. Tuy nhiên, dù mẫu có lớn bao nhiêu, sau khi định nghĩa bản ghi (và chức năng phụ trợ) chúng ta chỉ phải viết toR -> RNode{...}
.
Tôi không chắc chắn tôi thực sự thích điều này, mặc dù.
Đầu tiên, bản ghi gây ô nhiễm không gian tên chung với ba (một phần!) Chức năng left :: R -> Tree, n :: R -> Int, right :: R -> Tree
. Điều này sẽ kích hoạt một số cảnh báo nếu bạn cố gắng sử dụng ví dụ: n
làm đối số của một hàm không liên quan khác (The local name shadows the global name n
).
Thứ hai, phần mở rộng ký tự đại diện ghi lại phạm vi một số biến không được viết trong mã - người đọc phải biết (hoặc đoán) những biến này là gì. Cũng lưu ý rằng mẫu RNode{..}
mang lại ví dụ: n
thành phạm vi có loại khác với tên chung: Int
thay vì R -> Int
. Người đọc có thể nghĩ rằng n
là thiết bị toàn cầu và bị nhầm lẫn ngay cả ở cấp loại.
Thứ ba, các mã trên sử dụng xem mẫu, và những hiện nhầm lẫn giữa kiểm tra exhaustiveness:
Patterns.hs:23:1: Warning:
Pattern match(es) are non-exhaustive
In an equation for ‘add2’: Patterns not matched: _ _
Trong trường hợp cụ thể này, chúng tôi chỉ đơn giản là có thể viết
add2 :: Tree -> Int -> Tree
add2 (node -> RNode{..}) x = Node (n+x) (add2 left x) (add2 right x)
add2 _ x = Leaf
để tránh những cảnh báo, nhưng chúng tôi không luôn có lựa chọn đó, nói chung.
Nếu bạn sử dụng GHC, bạn có thể sử dụng [Record Wildcards] (http://downloads.haskell.org/~ghc/latest/docs/html/users_guide/syntax-extns.html#record-wildcards). – PyRulez