[英]Pattern matching on a private data constructor
我正在為網格軸編寫一個簡單的ADT。 在我的應用程序中,網格可以是常規的(在坐標之間有恆定的步長),也可以是不規則的(否則)。 當然,常規網格只是不規則網格的一種特殊情況,但在某些情況下可能需要區分它們(例如,執行一些優化)。 所以,我聲明我的ADT如下:
data GridAxis = RegularAxis (Float, Float) Float -- (min, max) delta
| IrregularAxis [Float] -- [xs]
但我不希望用戶使用max < min
或使用無序xs
列表創建格式錯誤的軸。 因此,我添加了“更智能”的構造函數 ,它們執行一些基本檢查:
regularAxis :: (Float, Float) -> Float -> GridAxis
regularAxis (a, b) dx = RegularAxis (min a b, max a b) (abs dx)
irregularAxis :: [Float] -> GridAxis
irregularAxis xs = IrregularAxis (sort xs)
我不希望用戶直接創建網格,所以我不將GridAxis
數據構造函數添加到模塊導出列表中:
module GridAxis (
GridAxis,
regularAxis,
irregularAxis,
) where
但事實證明,完成此操作后,我無法再使用GridAxis
上的模式匹配。 試着用它
import qualified GridAxis as GA
test :: GA.GridAxis -> Bool
test axis = case axis of
GA.RegularAxis -> True
GA.IrregularAxis -> False
給出以下編譯器錯誤:
src/Physics/ImplicitEMC.hs:7:15:
Not in scope: data constructor `GA.RegularAxis'
src/Physics/ImplicitEMC.hs:8:15:
Not in scope: data constructor `GA.IrregularAxis'
這有什么可以解決的嗎?
您可以定義構造函數模式同義詞。 這使您可以使用相同的名稱進行智能構造和“啞”模式匹配。
{-# LANGUAGE PatternSynonyms #-}
module GridAxis (GridAxis, pattern RegularAxis, pattern IrregularAxis) where
import Data.List
data GridAxis = RegularAxis_ (Float, Float) Float -- (min, max) delta
| IrregularAxis_ [Float] -- [xs]
-- The line with "<-" defines the matching behavior
-- The line with "=" defines the constructor behavior
pattern RegularAxis minmax delta <- RegularAxis_ minmax delta where
RegularAxis (a, b) dx = RegularAxis_ (min a b, max a b) (abs dx)
pattern IrregularAxis xs <- IrregularAxis_ xs where
IrregularAxis xs = IrregularAxis_ (sort xs)
現在你可以這樣做:
module Foo
import GridAxis
foo :: GridAxis -> a
foo (RegularAxis (a, b) d) = ...
foo (IrregularAxis xs) = ...
並且還使用RegularAxis
和IrregularAxis
作為智能構造函數。
這看起來像模式同義詞的用例。
基本上你不導出真正的構造函數,而只導出一個“智能”構造函數
{-# LANGUAGE PatternSynonyms #-}
module M(T(), SmartCons, smartCons) where
data T = RealCons Int
-- the users will construct T using this
smartCons :: Int -> T
smartCons n = if even n then RealCons n else error "wrong!"
-- ... and destruct T using this
pattern SmartCons n <- RealCons n
然后可以使用另一個導入M
模塊
case someTvalue of
SmartCons n -> use n
例如
let value = smartCons 23 in ...
但不能直接使用RealCons
。
如果您希望保留基本的Haskell,沒有擴展名,則可以使用“視圖類型”
module M(T(), smartCons, Tview(..), toView) where
data T = RealCons Int
-- the users will construct T using this
smartCons :: Int -> T
smartCons n = if even n then RealCons n else error "wrong!"
-- ... and destruct T using this
data Tview = Tview Int
toView :: T -> Tview
toView (RealCons n) = Tview n
在這里,用戶可以完全訪問視圖類型,可以自由地構造/銷毀,但是對於實際類型T
只有一個受限制的啟動構造函數。 通過移動到視圖類型可以破壞實際類型T
case toView someTvalue of
Tview n -> use n
對於嵌套模式,除非啟用其他擴展(如ViewPatterns
,否則事情會變得更加麻煩。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.