I have some record types structured essentially like this:
type Body x = { x | pos: (Int,Int) }
type Bubble = Body { radius: Int }
type Box = Body { width: Int, height: Int }
Now I would like to have a mixed list of any of these and perform some operation on the Body
part, but still special case handling Box
and Bubble
other times. For example, having (implementations elided):
mv: (Int,Int) -> Body a -> Body a
bubble: Bubble
box: Box
I would like to
map (mv (1,1)) [box,bubble]
but this fails because Elm deems the types in the list incompatible.
Now I could wrap the Box
es and Bubble
s in an ADT like so:
type BodyWrap = BoxWrap Box | BubbleWrap Bubble
but then I need to do an unwrapping and rewrapping in every case. If I want to fold on the mixed list it gets even messier. An example is in this gist.
Is there a more elegant way of handling this situation?
This problem goes away when using composition rather than inheritance.
Instead of wrapping the whole structure in an ADT, make one field in the record hold an ADT with the object-specific properties:
type Body = { pos: (Int,Int), shape: Shape }
data shape = Bubble Int | Box (Int,Int)
This allows using the shared structure in Body
while matching on shape
only when necessary.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With