Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to export constructors for pattern matching, but not for construction, in Haskell Modules?

A vanilla data type in Haskell has zero or more constructors, each of which plays two roles.

In expressions, it supports introduction, its a function from zero or more arguments to the data type.

In patterns, it supports elimination, its kinda like a function from the data type to Maybe (tuple of argument types).

Is it possible for a module signature to hide the former while exposing the latter?

The use case is this: I have a type, T, whose constructors types alone can sometimes be used to construct nonsense. I have construction functions which can be used to build instances of the type that are guaranteed not to be nonsense. It would make sense to hide the constructors in this case, but it would still be useful for callers to be able to pattern match over the guaranteed-non-nonsense that they build with the construction functions.

I suspect this is impossible, but in case anyone has a way to do it, I though I would ask.

Next best thing is to hide the constructors and create a bunch of functions from T -> Maybe (This, That), T -> Maybe (The, Other, Thing), etc.

like image 349
Doug McClean Avatar asked Nov 17 '11 18:11

Doug McClean


People also ask

Does Haskell have pattern matching?

Overview. We use pattern matching in Haskell to simplify our codes by identifying specific types of expression. We can also use if-else as an alternative to pattern matching. Pattern matching can also be seen as a kind of dynamic polymorphism where, based on the parameter list, different methods can be executed.

How does pattern matching work in Haskell?

Pattern matching consists of specifying patterns to which some data should conform and then checking to see if it does and deconstructing the data according to those patterns. When defining functions, you can define separate function bodies for different patterns.

Is Io a data constructor in Haskell?

IO is a type constructor, not a value constructor.


1 Answers

You can use a view type and view patterns to do what you want:

module ThingModule (Thing, ThingView(..), view) where  data Thing = Foo Thing | Bar Int  data ThingView = FooV Thing | BarV Int  view :: Thing -> ThingView view (Foo x) = FooV x view (Bar y) = BarV y 

Note that ThingView is not a recursive data type: all the value constructors refer back to Thing. So now you can export the value constructors of ThingView and keep Thing abstract.

Use like this:

{-# LANGUAGE ViewPatterns #-} module Main where  import ThingModule  doSomethingWithThing :: Thing -> Int doSomethingWithThing(view -> FooV x) = doSomethingWithThing x doSomethingWithThing(view -> BarV y) = y 

The arrow notation stuff is GHC's View Patterns. Note that it requires a language pragma.

Of course you're not required to use view patterns, you can just do all the desugaring by hand:

doSomethingWithThing :: Thing -> Int doSomethingWithThing = doIt . view   where doIt (FooV x) = doSomethingWithThing x         doIt (BarV y) = y 

More

Actually we can do a little bit better: There is no reason to duplicate all the value constructors for both Thing and ThingView

module ThingModule (ThingView(..), Thing, view) where     newtype Thing = T {view :: ThingView Thing}    data ThingView a = Foo a | Bar Int 

Continue useing it the same way as before, but now the pattern matches can use Foo and Bar.

{-# LANGUAGE ViewPatterns #-} module Main where  import ThingModule  doSomethingWithThing :: Thing -> Int doSomethingWithThing(view -> Foo x) = doSomethingWithThing x doSomethingWithThing(view -> Bar y) = y 
like image 111
3 revs Avatar answered Sep 23 '22 08:09

3 revs