Multi-param typeclass as superclass of Single-param typeclasses

This is my first post here, so Hello everyone! In haskell it is possible to express a constraint "if 'a' and 'b' are instance of 'AB' then 'a' is instance of 'A' and 'b' is instance of 'B'": class (A a, B b) => AB a b ... Is it possible to express the converse - "if 'a' is instance of 'A' and 'b' is instance of 'B' then 'a' and 'b' are instance of 'AB'"? I want to create a list of shapes that can be tested for intersection. I think that possibility of expressing such constraints would allow to doing this in a "Object Oriented" way: data Circle = Circle { ... } data Square = Square { ... } class Shape a class Intersect a b where intersect :: a -> b -> Bool -- pseudo haskell - "If 'a' is instance of 'Shape' and 'b' is instance of 'Shape' then 'a' and 'b' are instance of 'Intersect'" -- maybe some type hackery allows to express this? constraint Shape a, Shape b => Intersect a b instance Shape Circle instance Shape Square instance Intersect Circle Circle ... instance Intersect Circle Square ... ... data ShapeBox = forall a. Shape a => ShapeBox a foo = let x:y:_ = [ShapeBox Circle { ... }, ShapeBox Square { ... }, ...] in intersect x y -- this should work because we know for sure that there is an instance of Intersect for type of 'x' and type of 'y' Is such idea already described somewhere?

Welcome! You can certainly express the constraint you want. But you can't do what you're trying to do with ShapeBox. Let's take those two in reverse order. Before I start, though, an obligatory warning (with the assumption that you're new to Haskell, as well as this list): object orientation is usually not the best way to go in Haskell. I don't mean to shame you for the question; it's a good learning exercise. But in most practical cases plain old data and functions will serve you better than a bunch of ad-hoc typeclasses. So. We have an existential type, ShapeBox, that stores a Shape but hides what Shape it is. And it seems that you'd like to be able write something like: bar :: ShapeBox -> ShapeBox -> Bool bar (ShapeBox x) (ShapeBox y) = intersect x y The trouble is that the instance of intersect we call depends on the actual types of x and y, and we don't know those types (because we've hidden them on purpose). If x and y are both Circles we need to use the Intersect Circle Circle instance. If instead y is a Square we must use the Intersect Circle Square instance, which in principle could do something entirely different. But there's hope. The way to declare the constraint you're after is to put it on the instance declaration: instance (Shape a, Shape b) => Intersect a b where intersect x y = ... That says precisely that whenever a and b are shapes, you can intersect them. The catch is that you must write this instance using only the Shape-ness of x and y (which is all you know about them). So you won't be able to have one way to intersect squares and circles, and another for triangles and hexagons; you need to abstract all the knowledge that lets you intersect into the Shape class. The difference is between telling the compiler, "I promise I'll go and write a suitable instance for each pair of Shapes, really" (which Haskell won't allow), and saying "here's how to intersect any two shapes whatsoever." You may want to check out the diagrams package, which does something similar with the class HasEnvelope. Hope that helps, Ben On Thu, Oct 26, 2017 at 11:37 AM Tomasz Chronowski < chronowski.tomasz@gmail.com> wrote:
This is my first post here, so Hello everyone!
In haskell it is possible to express a constraint "if 'a' and 'b' are instance of 'AB' then 'a' is instance of 'A' and 'b' is instance of 'B'":
class (A a, B b) => AB a b ...
Is it possible to express the converse - "if 'a' is instance of 'A' and 'b' is instance of 'B' then 'a' and 'b' are instance of 'AB'"?
I want to create a list of shapes that can be tested for intersection. I think that possibility of expressing such constraints would allow to doing this in a "Object Oriented" way:
data Circle = Circle { ... } data Square = Square { ... }
class Shape a
class Intersect a b where intersect :: a -> b -> Bool
-- pseudo haskell - "If 'a' is instance of 'Shape' and 'b' is instance of 'Shape' then 'a' and 'b' are instance of 'Intersect'" -- maybe some type hackery allows to express this? constraint Shape a, Shape b => Intersect a b
instance Shape Circle instance Shape Square
instance Intersect Circle Circle ... instance Intersect Circle Square ... ...
data ShapeBox = forall a. Shape a => ShapeBox a
foo = let x:y:_ = [ShapeBox Circle { ... }, ShapeBox Square { ... }, ...] in intersect x y -- this should work because we know for sure that there is an instance of Intersect for type of 'x' and type of 'y'
Is such idea already described somewhere? _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

May be type families can help here? There was good article about Pokemons and type families which seems to be close to such task IMHO...
Welcome! You can certainly express the constraint you want. But you can't do what you're trying to do with ShapeBox.
Let's take those two in reverse order. Before I start, though, an obligatory warning (with the assumption that you're new to Haskell, as well as this list): object orientation is usually not the best way to go in Haskell. I don't mean to shame you for the question; it's a good learning exercise. But in most practical cases plain old data and functions will serve you better than a bunch of ad-hoc typeclasses.
So. We have an existential type, ShapeBox, that stores a Shape but hides what Shape it is. And it seems that you'd like to be able write something like:
bar :: ShapeBox -> ShapeBox -> Bool bar (ShapeBox x) (ShapeBox y) = intersect x y
The trouble is that the instance of intersect we call depends on the actual types of x and y, and we don't know those types (because we've hidden them on purpose). If x and y are both Circles we need to use the Intersect Circle Circle instance. If instead y is a Square we must use the Intersect Circle Square instance, which in principle could do something entirely different.
But there's hope. The way to declare the constraint you're after is to put it on the instance declaration:
instance (Shape a, Shape b) => Intersect a b where intersect x y = ...
That says precisely that whenever a and b are shapes, you can intersect them. The catch is that you must write this instance using only the Shape-ness of x and y (which is all you know about them). So you won't be able to have one way to intersect squares and circles, and another for triangles and hexagons; you need to abstract all the knowledge that lets you intersect into the Shape class.
The difference is between telling the compiler, "I promise I'll go and write a suitable instance for each pair of Shapes, really" (which Haskell won't allow), and saying "here's how to intersect any two shapes whatsoever."
You may want to check out the diagrams package, which does something similar with the class HasEnvelope.
Hope that helps,
Ben
On Thu, Oct 26, 2017 at 11:37 AM Tomasz Chronowski < chronowski.tomasz@gmail.com> wrote:
This is my first post here, so Hello everyone!
In haskell it is possible to express a constraint "if 'a' and 'b' are instance of 'AB' then 'a' is instance of 'A' and 'b' is instance of 'B'":
class (A a, B b) => AB a b ...
Is it possible to express the converse - "if 'a' is instance of 'A' and 'b' is instance of 'B' then 'a' and 'b' are instance of 'AB'"?
I want to create a list of shapes that can be tested for intersection. I think that possibility of expressing such constraints would allow to doing this in a "Object Oriented" way:
data Circle = Circle { ... } data Square = Square { ... }
class Shape a
class Intersect a b where intersect :: a -> b -> Bool
-- pseudo haskell - "If 'a' is instance of 'Shape' and 'b' is instance of 'Shape' then 'a' and 'b' are instance of 'Intersect'" -- maybe some type hackery allows to express this? constraint Shape a, Shape b => Intersect a b
instance Shape Circle instance Shape Square
instance Intersect Circle Circle ... instance Intersect Circle Square ... ...
data ShapeBox = forall a. Shape a => ShapeBox a
foo = let x:y:_ = [ShapeBox Circle { ... }, ShapeBox Square { ... }, ...] in intersect x y -- this should work because we know for sure that there is an instance of Intersect for type of 'x' and type of 'y'
Is such idea already described somewhere? _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
participants (3)
-
Baa
-
Ben Doyle
-
Tomasz Chronowski