Haskell constructor as variables of functions
There is, strictly speaking, no technical reason for GHC not to allow this kind of thing, except that it would only work when all constructors have parameters of the same type - otherwise you couldn't apply the same function to them.
And this gives us an insight: most of the time, discriminated union constructors have parameters of different types, like data Thing = Text String | Number Int
, and even if the types happen to be the same, that's usually just a coincidence, and the parameters actually have different meanings, like data Heisenberg = Velocity Vector2D | Position Vector2D
, so that it wouldn't make sense to apply the same function to them, even if technically possible.
This is the intended meaning of discriminated unions. The constructors are supposed to represent semantically different kinds of things.
And that's why GHC doesn't support this sort of syntax, even when the types match: it's outside of the intended use case, and most of the time it's useless, even if it could potentially be useful in some very narrow area.
But from your description of desired use cases, it kinda seems like you're trying to express a completely different thing: it looks like the a
in both A a
and B a
is meant to represent the same thing, while A
and B
are used merely as "tags", to express some attribute of the value, which is not inherent to the value itself. Like, for example, a
could be the size of a balloon, while A
and B
could represent two different colors that balloons can have.
If this is indeed what you're trying to express, then a better model would be to encode the tags as such instead of trying to shoehorn DU constructors to represent them, and then combine the tag with the value in a record:
data NewTag = A | B
data New a = New { tag :: NewTag, value :: a }
With this definition, both fun
and con
become trivial:
fun :: (a -> b) -> New a -> b
fun f n = f $ value n
con :: NewTag -> a -> New a
con tag value = New { tag = tag, value = value }
Or point-free if you're into that sort of thing:
fun :: (a -> b) -> New a -> b
fun f = f . value
con :: NewTag -> a -> New a
con = New
Addressing your first question...
You could try using record syntax:
data New a = A { get :: a } | B { get :: a }
Then fun
could be defined as
fun f x = f (get x)
If you make New
a Functor
...
instance Functor (New a)
where fmap f (A x) = A (f x)
fmap f (B x) = B (f x)
con
could be defined as
import Data.Functor (($>))
con x = (x $>)
if you enable the DeriveFunctor Language pragma
{-# LANGUAGE DeriveFunctor #-}
at the top of your file, you can avoid writing out the Functor
instance
data New a = A { get :: a } | B { get :: a } deriving (Functor)
Edit
Oops...or the obvious way (as in Fyodor's example)
con x y = x { get = y }