Pattern matching on a personal data constructor

I am writing a simple ADT for the grid axis. In my application grid, it can be either regular (with a constant step between coordinates) or irregular (otherwise). Of course, a regular grid is just a special case of an irregular grid, but in some situations it may be advisable to distinguish them (for example, to perform some optimizations). So, I declare my ADT as follows:

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

But I do not want the user to create invalid axes with max < min or with an unordered list xs . So, I am adding smart build functions that perform some basic checks:

 regularAxis :: (Float, Float) -> Float -> GridAxis regularAxis (a, b) dx = RegularAxis (min ab, max ab) (abs dx) irregularAxis :: [Float] -> GridAxis irregularAxis xs = IrregularAxis (sort xs) 

I don’t want the user to create meshes directly, so I don’t add GridAxis data GridAxis to the module export list:

 module GridAxis ( GridAxis, regularAxis, irregularAxis, ) where 

But it turned out that after that I can no longer use pattern matching on 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 anything for this?

+5
source share
2 answers

You can define synonyms for constructor templates. This allows you to use the same name for intelligent construction and "silent" matching patterns.

 {-# 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 ab, max ab) (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) = ... 

Also use RegularAxis and IrregularAxis as smart constructors.

+7
source

This looks like an example of using template synonyms .

Basically, you are not exporting a real constructor, but only 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 

Another module importing M can then use

 case someTvalue of SmartCons n -> use n 

and for example

 let value = smartCons 23 in ... 

but cannot use RealCons directly.


If you prefer to stay in base Haskell without extensions, you can use the "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 

Here, users have full access to the view type, which can be constructed / destroyed freely, but has only a limited start constructor for the actual type T Destroying the actual type T possible by going over to the type of view

 case toView someTvalue of Tview n -> use n 

For nested templates, things get more cumbersome unless you enable other extensions, such as ViewPatterns .

+3
source

Source: https://habr.com/ru/post/1235999/


All Articles