私有数据构造函数上的模式匹配
Pattern matching on a private data constructor
我正在为网格轴编写一个简单的 ADT。在我的应用程序中,网格可能是规则的(坐标之间的步长不变),也可能是不规则的(否则)。当然,规则网格只是不规则网格的特例,但在某些情况下(例如,执行某些优化)可能值得区分它们。所以,我声明我的 ADT 如下:
data GridAxis = RegularAxis (Float, Float) Float -- (min, max) delta
| IrregularAxis [Float] -- [xs]
但我不希望用户使用 max < min
或无序的 xs
列表创建格式错误的轴。所以,我添加 "smarter" construction functions 执行一些基本检查:
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'
有什么办法解决这个问题吗?
这看起来像是 pattern synonyms 的用例。
基本上你不导出真正的构造函数,而只导出一个"smart"
{-# 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,没有扩展,您可以使用 "view type"
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
在这里,用户可以完全访问视图类型,可以自由constructed/destructed,但对于实际类型只有一个受限的启动构造函数T
。通过移动到视图类型
可以破坏实际类型 T
case toView someTvalue of
Tview n -> use n
对于嵌套模式,事情变得更加麻烦,除非您启用其他扩展,例如 ViewPatterns
。
您可以定义构造函数模式同义词。这使您可以使用相同的名称进行智能构造和 "dumb" 模式匹配。
{-# 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
作为智能构造函数。
我正在为网格轴编写一个简单的 ADT。在我的应用程序中,网格可能是规则的(坐标之间的步长不变),也可能是不规则的(否则)。当然,规则网格只是不规则网格的特例,但在某些情况下(例如,执行某些优化)可能值得区分它们。所以,我声明我的 ADT 如下:
data GridAxis = RegularAxis (Float, Float) Float -- (min, max) delta
| IrregularAxis [Float] -- [xs]
但我不希望用户使用 max < min
或无序的 xs
列表创建格式错误的轴。所以,我添加 "smarter" construction functions 执行一些基本检查:
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'
有什么办法解决这个问题吗?
这看起来像是 pattern synonyms 的用例。
基本上你不导出真正的构造函数,而只导出一个"smart"
{-# 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,没有扩展,您可以使用 "view type"
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
在这里,用户可以完全访问视图类型,可以自由constructed/destructed,但对于实际类型只有一个受限的启动构造函数T
。通过移动到视图类型
T
case toView someTvalue of
Tview n -> use n
对于嵌套模式,事情变得更加麻烦,除非您启用其他扩展,例如 ViewPatterns
。
您可以定义构造函数模式同义词。这使您可以使用相同的名称进行智能构造和 "dumb" 模式匹配。
{-# 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
作为智能构造函数。