
The only bit I don't quite understand is why the following code implies an "or" relation in the type constraint of repr.
data Stroke = Line Point Point (forall repr. (CirclePen repr, RectPen repr) => repr) | Arc Point Point (forall repr. (CirclePen repr) => repr) | Spot Point (forall repr. (CirclePen repr, RectPen repr, ArbitraryPen repr) => repr)
To me this reads that repr should be both a CirclePen and a RectPen in order to satisfy the type constraint in the case of Line, but it seems that it is accepting a CirclePen or a RectPen (which is the desired behaviour, so I'm not complaining).
This point is indeed confusing. Perhaps yet another explanation might be helpful. Added to the already given, it might help reaching the critical mass. First, it seems that the dictionary passing implementation of type classes makes things clearer. This implementation realizes, informally, double arrow as a simple arrow. For example, the declaration
class CirclePen repr where circle :: Float -> repr
is translated to a dictionary declaration
data CirclePenDict repr = CirclePenDict{circle :: Float -> repr}
and a bounded polymorphic function or value like
CirclePen repr => repr is realized as CirclePenDict repr -> repr Double arrow turns simple arrow.
Therefore,
data Stroke = Line Point Point (forall repr. (CirclePen repr, RectPen repr) => repr)
becomes
data Stroke = Line Point Point (forall repr. (CirclePenDict repr, RectPenDict repr) -> repr)
We can make two different lines, with the circle pen shape:
-- Line p1 p2 (circle 10) stroke1 = Line' p0 p1 (\(circledict, rectdict) -> circle circledict 10)
or with the rectangular pen shape.
-- Line p1 p2 (circle 10 20) stroke2 = Line' p0 p1 (\(circledict, rectdict) -> rectangle rectdict 10 20)
Obviously we cannot make Lines with the Arbitrary pen shape since we will receive from the user only circledict and rectdict but no arbitrarypendict. Suppose we are communicating over a network. If I can send you _either_ a boolean or an integer, you have to be prepared to handle _both_. From a different point of view: if you send me the handler for a boolean _and_ the handler for an integer, it becomes my choice which one to invoke. (Incidentally, what I just described is a subset of session types for pi-calculus.) At this point de Morgan laws may spring to mind. Here is a related explanation of using open records to implement open unions. http://okmij.org/ftp/Haskell/generics.html#PolyVariant