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.
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.
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.
IO is a type constructor, not a value constructor.
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With