How to specialize a type

Hello all, Suppose I have a type "Process" which stands either for a Train or a moving Belt. In both cases there is a place of departure and a place of arrival. However the other parameters which describe a Process differ. A Train has departure and arrival times, but a Belt has a speed. I tried the following: type PlaceDep = Int type PlaceArr = Int data Process = Train PlaceDep PlaceArr TP | MovingBelt PlaceDep PlaceArr BP deriving (Eq, Show) data TP = TP Int deriving (Eq, Show) data BP = BP Int deriving (Eq, Show) prc1 = Train 10 11 (TP 1) prc2 = MovingBelt 12 13 (BP 2) What I don't like about this is that the fact that all Processes have PlaceDep and PlaceArr appears somewhat "coincidental". This in contrast, captures the common parts more clearly: type PlaceDep = Int type PlaceArr = Int data ProcessParams = DepartureTime Int | Speed Int deriving (Eq, Show) data Process = Process PlaceDep PlaceArr ProcessParams deriving (Eq, Show) prc1 = Process 10 11 (DepartureTime 1) prc2 = Process 12 13 (Speed 2) Is this the classic way of specializing a type or are there better options See also: http://stackoverflow.com/questions/33156656/subclassing-a-type-in-haskell/33...

Hi. You stack-overflow had good answers, I don't think I will shed new light on this. But still I will give it a try. On 16/10/15 04:32, martin wrote:
Hello all,
Suppose I have a type "Process" which stands either for a Train or a moving Belt. In both cases there is a place of departure and a place of arrival. However the other parameters which describe a Process differ. A Train has departure and arrival times, but a Belt has a speed.
I tried the following:
type PlaceDep = Int type PlaceArr = Int
data Process = Train PlaceDep PlaceArr TP | MovingBelt PlaceDep PlaceArr BP deriving (Eq, Show)
data TP = TP Int deriving (Eq, Show) data BP = BP Int deriving (Eq, Show)
You could use a newtype here. This is the intended use case of them as you want a semantically different type but an underlying same representation (and possible some instances of such representation). Something like this newtype TP = TP Int deriving (Eq, Show) newtype BP = BP Int deriving (Eq, Show)
prc1 = Train 10 11 (TP 1) prc2 = MovingBelt 12 13 (BP 2)
What I don't like about this is that the fact that all Processes have PlaceDep and PlaceArr appears somewhat "coincidental".
This in contrast, captures the common parts more clearly:
type PlaceDep = Int type PlaceArr = Int
data ProcessParams = DepartureTime Int | Speed Int deriving (Eq, Show)
data Process = Process PlaceDep PlaceArr ProcessParams deriving (Eq, Show)
prc1 = Process 10 11 (DepartureTime 1) prc2 = Process 12 13 (Speed 2)
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
Is this the classic way of specializing a type or are there better options
I would just stick to sum types (the first approach) but I don't know the whole context of the problem. BTW "specializing a type" has a concrete meaning in haskell. It grabs a polymorphic function and gives it a more concrete type. An usual example is id :: a -> a id x = x id2 :: Char -> Char id2 c = id c which hopefully is clear on what I am going about

Am 10/17/2015 um 08:08 AM schrieb Ruben Astudillo:
data Process = Train PlaceDep PlaceArr TP | MovingBelt PlaceDep PlaceArr BP deriving (Eq, Show)
data TP = TP Int deriving (Eq, Show) data BP = BP Int deriving (Eq, Show)
You could use a newtype here.
Point taken
What I don't like about this is that the fact that all Processes have PlaceDep and PlaceArr appears somewhat "coincidental".
This in contrast, captures the common parts more clearly:
type PlaceDep = Int type PlaceArr = Int
data ProcessParams = DepartureTime Int | Speed Int deriving (Eq, Show)
data Process = Process PlaceDep PlaceArr ProcessParams deriving (Eq, Show)
prc1 = Process 10 11 (DepartureTime 1) prc2 = Process 12 13 (Speed 2)
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.

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
participants (2)
-
martin
-
Ruben Astudillo