Reputation: 2210
I have a custom data type to contain elements of a landscape (cloud, sun, mountain, etc). I need to have a list of them, so I can't use different types and a common typeclass.
They share most of the fields in the constructor, however some have properties that other don't (for example, if the cloud is raining or not).
As of now, I have one data type with different constructors:
data Element = Sun {
elemColorStart :: Color,
elemColorEnd :: Color,
elemCoords :: Coords,
elemPeriod :: Float,
elemSize :: Float,
elemSteps :: Step,
elemTime :: Float
}
| Cloud {
elemKind :: CloudKind,
elemColorMain :: Color,
elemCoords :: Coords,
elemRans :: [Float],
elemSize' :: Size',
elemSteps :: Step,
elemTime :: Float
}
... etc
Alternatively I could have a common constructor with all the possible properties, and just not initialise them if theyre not needed, though this looks even worse.
This does not look very "Haskelly", and in fact this approach in general is quite object oriented. What am I doing wrong? Any other possible approach would be welcome; it is not an assignment so I don't really have any constraints.
Upvotes: 1
Views: 253
Reputation: 3504
A twist on Nikita's answer is to use an ADT called Shared
holding fields found in both Sun
and Cloud
. If you want you can compare this to inheritance from OO.
data Shared = Shared {
coords :: Coords,
steps :: Step,
time :: Float
}
data Sun = Sun {
colorStart :: Color,
colorEnd :: Color,
period :: Float,
size :: Float,
shared :: Shared
}
data Cloud = Cloud {
kind :: CloudKind,
colorMain :: Color,
rans :: [Float],
size :: Size',
shared :: Shared
}
data Element = SunElement Sun | CloudElement Cloud
Upvotes: 1
Reputation: 43309
Isolate the things into their own datatypes and have Element
be a sum of them:
data Sun = Sun {
colorStart :: Color,
colorEnd :: Color,
coords :: Coords,
period :: Float,
size :: Float,
steps :: Step,
time :: Float
}
data Cloud = Cloud {
kind :: CloudKind,
colorMain :: Color,
coords :: Coords,
rans :: [Float],
size :: Size',
steps :: Step,
time :: Float
}
data Element = SunElement Sun | CloudElement Cloud
Now you can have dedicated APIs for things in isolation as much as all the good things that come with separation of concerns.
Oh and BTW, I've dropped the prefixes on field names, since now we have the DuplicateRecordFields
extension.
BTW, you'll find this question useful.
Upvotes: 8