簡體   English   中英

私有數據構造函數上的模式匹配

[英]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) = ...

並且還使用RegularAxisIrregularAxis作為智能構造函數。

這看起來像模式同義詞的用例。

基本上你不導出真正的構造函數,而只導出一個“智能”構造函數

{-# 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM