简体   繁体   English

私有数据构造函数上的模式匹配

[英]Pattern matching on a private data constructor

I'm writing a simple ADT for grid axis. 我正在为网格轴编写一个简单的ADT。 In my application grid may be either regular (with constant step between coordinates), or irregular (otherwise). 在我的应用程序中,网格可以是常规的(在坐标之间有恒定的步长),也可以是不规则的(否则)。 Of course, the regular grid is just a special case of irregular one, but it may worth to differentiate between them in some situations (for example, to perform some optimizations). 当然,常规网格只是不规则网格的一种特殊情况,但在某些情况下可能需要区分它们(例如,执行一些优化)。 So, I declare my ADT as the following: 所以,我声明我的ADT如下:

data GridAxis = RegularAxis (Float, Float) Float -- (min, max) delta
              | IrregularAxis [Float]            -- [xs]

But I don't want user to create malformed axes with max < min or with unordered xs list. 但我不希望用户使用max < min或使用无序xs列表创建格式错误的轴。 So, I add "smarter" construction functions which perform some basic checks: 因此,我添加了“更智能”的构造函数 ,它们执行一些基本检查:

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)

I don't want user to create grids directly, so I don't add GridAxis data constructors into module export list: 我不希望用户直接创建网格,所以我不将GridAxis数据构造函数添加到模块导出列表中:

module GridAxis (
    GridAxis,
    regularAxis,
    irregularAxis,
    ) where

But it turned out that after having this done I cannot use pattern matching on GridAxis anymore. 但事实证明,完成此操作后,我无法再使用GridAxis上的模式匹配。 Trying to use it 试着用它

import qualified GridAxis as GA

test :: GA.GridAxis -> Bool
test axis = case axis of
              GA.RegularAxis -> True
              GA.IrregularAxis -> False  

gives the following compiler error: 给出以下编译器错误:

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'

Is there something to work this around? 这有什么可以解决的吗?

You can define constructor pattern synonyms. 您可以定义构造函数模式同义词。 This lets you use the same name for smart construction and "dumb" pattern matching. 这使您可以使用相同的名称进行智能构造和“哑”模式匹配。

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

Now you can do: 现在你可以这样做:

module Foo
import GridAxis

foo :: GridAxis -> a
foo (RegularAxis (a, b) d) = ...
foo (IrregularAxis xs) = ...

And also use RegularAxis and IrregularAxis as smart constructors. 并且还使用RegularAxisIrregularAxis作为智能构造函数。

This looks as a use case for pattern synonyms . 这看起来像模式同义词的用例。

Basically you don't export the real constructor, but only a "smart" one 基本上你不导出真正的构造函数,而只导出一个“智能”构造函数

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

Another module importing M can then use 然后可以使用另一个导入M模块

case someTvalue of
   SmartCons n -> use n

and eg 例如

let value = smartCons 23 in ...

but can not use the RealCons directly. 但不能直接使用RealCons


If you prefer to stay in basic Haskell, without extensions, you can use a "view type" 如果您希望保留基本的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

Here, users have full access to the view type, which can be constructed/destructed freely, but have only a restricted start constructor for the actual type T . 在这里,用户可以完全访问视图类型,可以自由地构造/销毁,但是对于实际类型T只有一个受限制的启动构造函数。 Destructing the actual type T is possible by moving to the view type 通过移动到视图类型可以破坏实际类型T

case toView someTvalue of
  Tview n -> use n

For nested patterns, things become more cumbersome, unless you enable other extensions such as ViewPatterns . 对于嵌套模式,除非启用其他扩展(如ViewPatterns ,否则事情会变得更加麻烦。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM