I'm currently playing around with ADTs in Haskell and try to build an ADT Figure
:
data Figure = Rect { x :: Integer, y :: Integer, width :: Integer, height :: Integer}
| Circle { x :: Integer, y :: Integer, radius :: Integer}
| CombiFigure Figure Figure
deriving (Eq, Show, Read)
Now I came across the question how to implement a function that should not accept every Figure
, but e.g. only a Circle
.
Do I already have a bad design? Or is there some best-practice how to do this?
As example, think about a diameter function. All that came to my mind (I'm a complete beginner in Haskell) are the following two options, using undefined
or Maybe
:
1:
diameter :: Figure -> Integer
diameter (Circle _ _ r) = 2 * r
diameter _ = undefined
2:
diameter :: Figure -> Maybe Integer
diameter (Circle _ _ r) = Just (2 * r)
diameter _ = Nothing
Are there more preferable ways on how to accomplish that? Thanks!
You are correct that there is something not right here. The best way of thinking about it would be to start at the function diameter
and decide what it's type should ideally be. You would likely come up with
diameter :: Circle -> Integer
diameter (Circle _ _ r) = 2 * r
because diameters are only defined for circles.
This means that you will have to augment your data structure by splitting out Circle (and Rect too):
data Figure = RectFigure Rect
| CircleFigure Circle
| CombiFigure Figure Figure
deriving (Eq, Show, Read)
data Rect = Rect { rectX :: Integer, rectY :: Integer, rectWidth :: Integer, height :: Integer}
deriving (Eq, Show, Read)
data Circle = Circle { circleX :: Integer, circleY :: Integer, circleRadius :: Integer}
deriving (Eq, Show, Read)
which is nice because it is now more flexible: you can write functions that don't care what Figure
they are applied to, and you can write functions that are defined on specific Figure
s.
Now, if we are in a higher-up function and have a reference to a Figure
and we want to compute its diameter
if it's a CircleFigure
, then you can use pattern matching to do this.
Note: using undefined
or exceptions (in pure code) is likely a code smell. It could probably be solved by rethinking your types. If you have to indicate failure, then use Maybe
/Either
.