
Perimeter doesn't make sense for Sphere or Cylinder. So we could define a type class for objects that have perimeter and make an instance of it only for Circle (data Circle = Circle Position Radius). Make sense. But these three functions above have desired behaviour. If user has a list of objects like [Sphere, Circle, Circle, Cylinder] he would like to calculate perimeters of each object using map perimerer list (in this case we also have to modify Geometry data type). So we could make instances of "perimeter" type class for all objects and return zero in case if perimeter doesn't make sense. Same as previous version but with typeclasses and with additional constructors (constructors for each type of object + constructors in Geometry data). Looks a bit overcomplicated. Any reasons to use type classes in this case? Maybe there is something I'm missing?
If you're talking about a single datatype with multiple constructors, then the function 'perimeter :: Geometry -> Maybe Double' makes sense. If you're talking about multiple datatypes, then you probably want to go type class route. data Sphere = Sphere ... data Circle = Circle ... class Perimeter a where perimeter :: a -> Double instance Perimeter Circle where perimeter (Circle ...) = ... -- No instance for Sphere class Volume a where volume :: a -> Double instance Volume Sphere where volume (Sphere ...) = ... -- No instance for Circle You have to decide whether (1) a datatype Geometry makes sense or (2) a datatype per geometric entity is better. One advantage to #1 is that writing functions over the datatype is easy. One advantage to #2 is that you have fewer (partial) 'Maybe' functions. This is also related to the "expression problem," a Googleable term. As for having a list of objects, you can do it with either approach. The second approach may require existential types. Regards, Sean