
On 17/10/15 07:40, martin wrote:
Am 10/17/2015 um 08:08 AM schrieb Ruben Astudillo:
This just seems a trade-off if you want to know with what are you dealing just matching the outer constructor or more of the problem won't care about it. Not really a dichotomy worth caring about much anyways
I'll have to think about this. Wouldn't this be a problem when writing functions which operate on a Process of any type? Like e.g.
changeDeparture :: PlaceDep -> Process -> Process
I would have to pattern match against all the constructors, wouldn't I? If I don't have just two Processes types, but 20 of them, wouldn't that become messy?
In the second approach I only have to match Process and I can be certain there is a PlaceDep to be altered.
That is the tradeoff between caring more if they are a belt/train or you don't except in some functions which can pay the price of pattern matching on inner constructors. If you have many functions as changeDeparture I would go with the approach you say, but otherwise I would go with the first just because seems clearer to me. This is a question of "what you problem requires more" than a "always use this option" thing. What I can tell you is what other options you have at your disposal so you can consider them. A common option is to provide on the module the data type is defined the only functions that operate directly on the constructors such that every other operation can be implemented as composition of such functions. The classical example is FIFOs where pop :: FIFO a -> Maybe a peek :: FIFO a -> Maybe a push :: a -> FIFO a -> FIFO a Would be the only functions allowed to pattern match on the constructors for them you are sure they don't leak the underlying implementation (you want abstraction) and they give you the semantics of the FIFO. This way you can omit the constructors from the exports of the module and get function that operate of all the sum branches without explicit pattern matching. There is other more fine alternative in GHC called ViewPatterns[1] & PatternSynonyms[2]. The first does something like what Data.Sequence does with their view and ViewL datatype. The second is probably more interesting to your use case. It provides an alternative "pattern" which you can see as an alternate constructor for both branches of your data. So in your example you could write if you go with your last alternative (I hope I get this right). pattern Train a b (TP c) = Process a b (DepartureTime c) pattern Belt a b (BP c) = Process a b (Speed c) Which let you use Train/Belt as constructor in the functions you want on top of the usual Process constructor which you probably want to use more commonly. [1]: https://ghc.haskell.org/trac/ghc/wiki/ViewPatterns [2]: https://ghc.haskell.org/trac/ghc/wiki/PatternSynonyms