10

我正在为网格轴编写一个简单的 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'

有什么可以解决的吗?

4

2 回答 2

14

您可以定义构造函数模式同义词。这使您可以使用相同的名称进行智能构造和“哑”模式匹配。

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

并且还使用RegularAxisandIrregularAxis作为智能构造函数。

于 2015-11-15T17:31:19.540 回答
5

这看起来是模式同义词的用例。

基本上你不会导出真正的构造函数,而只是一个“智能”的构造函数

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

在这里,用户可以完全访问可以自由构造/破坏的视图类型,但对于实际类型只有一个受限的启动构造函数TT通过移动到视图类型可以破坏实际类型

case toView someTvalue of
  Tview n -> use n

对于嵌套模式,事情变得更加麻烦,除非您启用其他扩展,例如ViewPatterns.

于 2015-11-15T17:29:24.993 回答