Multi-param typeclass as superclass of Single-param typeclasses

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Multi-param typeclass as superclass of Single-param typeclasses

Tomasz Chronowski
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.
Reply | Threaded
Open this post in threaded view
|

Re: Multi-param typeclass as superclass of Single-param typeclasses

Ben Doyle
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 <[hidden email]> 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.

_______________________________________________
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.
Baa
Reply | Threaded
Open this post in threaded view
|

Re: Multi-param typeclass as superclass of Single-param typeclasses

Baa
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 <
> [hidden email]> 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.  

_______________________________________________
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.