Haskell constructor aliases
Asked Answered
P

2

5

Is there a way to have something equivalent to creating "constructor aliases" in Haskell? I'm thinking similar to type aliases where you can give the type a different name but it still behaves in every way as the aliased type.

My use case is a system where I have an assigned time as a property of some objects I'm modelling, so UTCTime. Some of these could be "variable" times, meaning it might not yet be assigned a time or the time it does have is "movable". So Maybe UTCTime.

But only some of the objects have variable times. Others have fixed times that the system has to take as a constant; a time variable currently assigned to a particular time is not handled the same way as a fixed time. Which now suggests Either UTCTime (Maybe UTCTime); it's either a fixed time or a variable time that might be unassigned.

The generic types seem to fit what I'm trying to model really well, so using them feels natural. But while it's obvious what Either UTCTime (Maybe UTCTime) is, it's not particularly obvious what it means, so some descriptive special-case names would be nice.

A simple type Timeslot = Either UTCTime (Maybe UTCTime) would definitely clean up my type signatures a lot, but that does nothing for the constructors. I can use something like bound = Just to get a name for constructing values, but not for pattern matching.

At the other end I can define a custom ADT with whatever names I want, but then I lose all the predefined functionality of the Either and Maybe types. Or rather I'll be applying transformations back and forth all the time (which I suppose is no worse than the situation with using newtype wrappers for things, only without the efficiency guarantee, but I doubt this would be a bottleneck anyway). And I suppose to understand code using generic Either and Maybe functions to manipulate my Timeslot values I'll need to know the way the standard constructors are mapped to whatever I want to use anyway, and the conversion functions would supply a handy compiler-enforced definition of that mapping. So maybe this is a good approach afterall.

I'm pretty sure I know Haskell well enough to say that there is no such thing as constructor-aliasing, but I'm curious whether there's some hack I don't know about, or some other good way of handling this situation.

Poinsettia answered 28/9, 2013 at 23:52 Comment(0)
P
4

The 'pattern synonyms' might get merged into ghc: http://ghc.haskell.org/trac/ghc/ticket/5144. In the meantime there is also -XViewPatterns, which lets you write things like:

type Timeslot = Either UTCTime (Maybe UTCTime)
fieldA = either Just (const Nothing)
fieldB = either (const Nothing) id


f (fieldA -> Just time) = ...
f (fieldB -> Just time) = ...
f _ = ...
Pyrex answered 29/9, 2013 at 1:9 Comment(2)
@aavgot - Don't you mean view patterns (ghc.haskell.org/trac/ghc/wiki/ViewPatterns)?Doykos
@Doykos my example code used -XViewPatterns as a verbose-but-currently-possible substitute for -XPatternSynonyms. -XPatternSynonyms was merged and can do exactly what OP asked.Pyrex
I
18

Despite the drawbacks you mentioned, I strongly suggest simply creating a fresh ADT for your type; for example

data TimeVariable = Constant UTCTime | Assigned UTCTime | Unassigned

I offer these arguments:

  1. Having descriptive constructors will make your code -- both construction and pattern matching -- significantly more readable. Compare Unassigned and Right Nothing. Now add six months and do the same comparison.
  2. I suspect that as your application grows, you will find that this type needs to expand. Adding another constructor or another field to an existing constructor is much easier with a custom ADT, and it makes it very easy to identify code locations that need to be updated to deal with the new type.
  3. Probably there will not be quite as many sensible operations on this type as there are in the standard library for munging Either and Maybe values -- so I bet you won't be duplicating nearly as much code as you think. And though you may be duplicating some code, giving your functions descriptive names is valuable for the same readability and refactoring reasons that giving your constructors descriptive names is.
  4. I have personally written some code where all my sums were Either and all my products were (,). It was horrible. I could never remember which side of a sum meant which thing; when reading old code I had to constantly remind myself what conceptual type each value was supposed to be (e.g. Right doesn't tell you whether you're using Right here as part of a time variable or part of some other thing that you were too lazy to make an ADT for); I had to constantly mentally expand type aliases; etc. Learn from my pain. ;-)
Ivonneivor answered 29/9, 2013 at 0:7 Comment(0)
P
4

The 'pattern synonyms' might get merged into ghc: http://ghc.haskell.org/trac/ghc/ticket/5144. In the meantime there is also -XViewPatterns, which lets you write things like:

type Timeslot = Either UTCTime (Maybe UTCTime)
fieldA = either Just (const Nothing)
fieldB = either (const Nothing) id


f (fieldA -> Just time) = ...
f (fieldB -> Just time) = ...
f _ = ...
Pyrex answered 29/9, 2013 at 1:9 Comment(2)
@aavgot - Don't you mean view patterns (ghc.haskell.org/trac/ghc/wiki/ViewPatterns)?Doykos
@Doykos my example code used -XViewPatterns as a verbose-but-currently-possible substitute for -XPatternSynonyms. -XPatternSynonyms was merged and can do exactly what OP asked.Pyrex

© 2022 - 2024 — McMap. All rights reserved.