假設我有一個 sum 型別(或多個,實際上),我知道設計上都有一個公共欄位:
data T1 a
= C1 String a
| C2 Int a
| C3 Bool a
data T2 a
= C4 Int Int a
| C5 [String] a
有沒有一種方法可以訪問該a欄位而無需對所有型別的所有變體進行模式匹配?
(我在定義 AST 的背景關系中詢問并以一種簡潔的方式訪問特定于節點的資訊)
uj5u.com熱心網友回復:
一個小的技術細節
在無聊的超技術水平上,沒有。如果沒有模式匹配,就無法訪??問建構式的欄位。模式匹配是導致建構式被評估的原始操作。在此之前,這些欄位甚至不一定存在(感謝非嚴格評估)。
一些可能有用的選項
但你可能不是指那個低級的問題。您可能想要一種方法來處理這些資料型別,而無需不斷撰寫模式匹配。這是可以做到的。這只是撰寫一些函式的問題。但是,哪些功能?...這可能很有趣。
您可以撰寫簡單的訪問器函式:
t1ToA :: T1 a -> a
t1ToA (C1 _ x) = x
t1ToA (C2 _ x) = x
t1ToA (C3 _ x) = x
t2ToA :: T2 a -> a
t2ToA (C4 _ _ x) = x
t2ToA (C5 _ x) = x
不要自動拒絕這種方法。當然,命名空間有點餓,因為每種型別都需要不同的函式名。另一方面,它對可讀性和型別推斷非常有用。任何地方都沒有什么神奇的。您也可以撰寫一些匹配的 setter 和 modifier 函式。
如果您在添加了各種設定和修改功能時發現命名空間過于匱乏,您可以使用 van Laarhoven 技巧:
t1A :: Functor f => (a -> f a) -> T1 a -> f (T1 a)
t1A g (C1 x y) = C1 x <$> g y
t1A g (C2 x y) = C2 x <$> g y
t1A g (C3 x y) = C3 x <$> g y
t2A :: Functor f => (a -> f a) -> T2 a -> f (T2 a)
t2A g (C4 x y z) = C4 x y <$> g z
t2A g (C5 x y) = C5 x <$> g y
這種表示允許您從同一型別進行讀取和更新,盡管如果沒有一些輔助函式會很尷尬。這是lens等庫使用的表示形式,它為您提供了大量的輔助函式。但也許您不想擔心學習如何使用這種表示。我將假設這不是您真正想要的,甚至不會詳細介紹這些輔助函式的作業原理。但在高層次上,它們巧妙地使用了flikeIdentity和的特定型別Const a。
如果您愿意放棄某些型別推斷以減少名稱空間的使用,那么一個選項是選擇某種臨時類:
class ToA f where
toA :: f a -> a
instance ToA T1 where
toA (C1 _ x) = x
toA (C2 _ x) = x
toA (C3 _ x) = x
instance ToA T2 where
toA :: T2 a -> a
toA (C4 _ _ x) = x
toA (C5 _ x) = x
您可以選擇將其與 van Laarhoven 編碼結合使用,以確保其物有所值。這將最大限度地減少您獲取的命名空間的數量,但需要一些額外的幫助程式才能輕松使用它們。
您可能還可以使用其他一些選項,例如使用 GHC 提供的較少的臨時工具。Data并且Generic是您可以使用的不同類,GHC 已經為您提供了很多工具。但這些往往是非常復雜的第一次拿起。
但也許有更好的解決方案
最后一個選項實際上是我在大多數情況下推薦的選項。重構您的資料型別,使共享值不會重復。
data WithA t a = WithA t a
data T1
= C1 String
| C2 Int
| C3 Bool
等等。或者,您可能會選擇重構它。重要的部分是共享欄位從 sum 型別中提取出來,并且始終存在。我認為這通常最終效果最好。它通常可以更好地傳達您的意思。當您有 3 個建構式,每個建構式都有一個相同型別的欄位時,即使資料型別在該欄位的型別上是多型的,也不能立即看出該欄位應該在建構式之間被視為可互換的。但是,如果它是多個建構式之外的單個欄位,那么很明顯它總是相同的。不要低估為所有未來的代碼維護者提供的交流價值。
uj5u.com熱心網友回復:
卡爾已經提到了一些替代方案。為了完整起見,讓我補充一點,使用記錄允許為所有建構式使用公共欄位名稱,而這反過來又允許獲取/設定公共欄位。
data T a
= K1 { foo :: a, oth1 :: Int }
| K2 { foo :: a, oth2 :: String }
get :: T a -> a
get = foo
set :: T a -> a -> T a
set t x = t{foo = x}
我不是這種方法的忠實擁護者,因為其他領域(例如oth1, oth2成為部分功能)。我寧愿重構 Carl 展示的型別(WithA示例)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/528541.html
標籤:哈斯克尔
上一篇:是否可以在haskell中定義一個接受一個引數、忽略它并回傳自身的函式?
下一篇:創建自定義類的實體
