Heterogeneous polymorphism in Haskell (correct way)
class, haskell, heterogeneous, polymorphism, types
Solution
Your existential solution is okay. It might be "prettier" to instead use a `GADT`, as in:
{-# LANGUAGE GADTs #-}
data Shape where
Shape :: (Surface a) => a -> Shape
...and as leftaraoundabout suggests, you may be able to structure your code differently.
But I think you've basically hit up against the Expression Problem here; or perhaps, more accurately: by trying to structure your code cleverly (separate type for each shape with classes) in anticipation of the EP you've introduced new difficulties for yourself.
Check out the fun Data Types a la Carte by Wouter Swierstra for an elegant solution to what I hope is related to your problem. Maybe someone can comment with good packages on hackage to look at that are inspired by that paper.
Problem
Let a module to abstract `Area` operations (bad definition) ``` class Area someShapeType where area :: someShapeType -> Float -- module utilities sumAreas :: Area someShapeType => [someShapeType] sumAreas = sum . map area ``` Let a posteriori explicit shape type modules (good or acceptable definition) ``` data Point = Point Float Float data Circle = Circle Point Float instance Surface Circle where surface (Circle _ r) = 2 * pi * r data Rectangle = Rectangle Point Point instance Surface Rectangle where surface (Rectangle (Point x1 y1) (Point x2 y2)) = abs $ (x2 - x1) * (y2 - y1) ``` Let some data ``` c1 = Circle (Point 0 0) 1 r1 = Rectangle (Point 0 0) (Point 1 1) ``` Then, trying to use ``` totalArea = sumAreas [c1, r1] ``` the `[c1, r1]` type must be expanded to `[Circle]` or `[Rectangle]`! (and is not valid) I can do using `forall` and a extra `data` type like this ``` data Shape = forall a . Surface a => Shape a sumSurfaces :: [Shape] -> Float sumSurfaces = sum . map (\(Shape x) -> surface x) ``` then, next code run successfully ``` sumSurfaces [Shape c1, Shape r1] ``` but I think, the use of `data Shape` and `Shape` constructor (on `[Shape c1, ...]` and lambda argument) is ugly (my first [and bad] way is pretty). What is the correct way to do "Heterogeneous polymorphism in Haskell"? Thank you very much for your time!