Using subclass implementation in the definition of superclass functions - haskell

In my Haskell program I have some typeclasses representing abstract notions of "shapes", namely
-- | Class representing shapes.
class Shape a where
isColliding :: (Shape b) => a -> b -> Bool
centroid :: Point
-- | Class representing shapes composed of a finite number of vertices
and line segments connecting them.
class (Shape a) => Polygon a where
vertices :: a -> Vertices
As you can see, Polygon is naturally a subclass of Shape. I also have some data types that are instances of these different typeclasses. For example:
data Box = Box Point Point Angle
instance Shape Box where
...
instance Polygon Box where
...
---------------------------------
data Circle = Circle Point Radius
instance Shape Circle where
...
I have many more possible shapes, such as NGon, RegularNGon, etc. I would like to be able to implement isColliding, but the information required to calculate whether two shapes are colliding is dependent upon the implementation of the specific instance of Shape. For example, to calculate if two boxes are colliding, I need their list of vertices. So I have a few questions:
Is there anyway to "specialize" my function isColliding so that it is defined in a specific way for collisions of the type isColliding :: (Polygon b) => Box -> b -> Bool?
Is the structuring of my datatypes the best way to approach this problem, or am I misusing typeclasses and datatypes when the whole thing could be restructured to eliminate this problem?
I am rather new to Haskell, so if my question is worded poorly or any clarification is needed, please tell me.

Your current Shape class says “isColliding can tell whether this shape intersects another shape using only the methods of Shape on the other shape”, because its signature (Shape b) => a -> b -> Bool only tells you that b has an instance of Shape. So you’re right that this isn’t quite what you want.
One thing you can do is use MultiParamTypeClasses to describe a relationship between two types:
{-# LANGUAGE MultiParamTypeClasses #-}
class Colliding a b where
collidesWith :: a -> b -> Bool
And then make instances for various concrete combinations of types:
instance Colliding Circle Box where
Circle p r `collidesWith` Box p1 p2 θ = {- … -}
Here you know the concrete types of both a and b when defining the implementation. That might be good enough for your use case.
However, this leaves you with n2 instances if you have n types. And you’ll run into problems if you try to define polymorphic instances like this:
instance (HasBoundingBox b) => Colliding Circle b where
collidesWith = {- … -}
Because this overlaps with all your other instances for Colliding Circle: b will match any type, and only add the constraint that b must have an instance of HasBoundingBox. That constraint is checked after instance resolution. You can work around this with OverlappingInstances or the newer OVERLAPPABLE/OVERLAPPING/OVERLAPS pragmas to tell GHC to choose the most specific matching instance, but this might be more trouble than it’s worth if you’re just getting familiar with Haskell.
I’d have to think on it more, but there are definitely alternative approaches. In the simplest case, if you only need to deal with a few different kinds of shape, then you can just make them a single sum type instead of separate data types:
data Shape
= Circle Point Radius
| Box Point Point Angle
| …
Then your isColliding function can be of type Shape -> Shape -> Bool and just pattern-match on this type.
Generally speaking, if you’re writing a typeclass, it should come with laws for how instances should behave, like mappend x mempty == mappend mempty x == x from Data.Monoid. If you can’t think of any equations that should always hold for instances of your class, you should prefer to represent things with plain old functions and data types instead.

Related

Creating list of values of the same typeclass but different types

I'm new to Haskell and trying to do something which I'm sure is easy but I'm not seeing the right way to do it.
What I want is a list of values of a particular typeclass, but different types of that typeclass. Eg:
class Shape a where
area :: a -> Double
numVertices :: a -> Integer
data Triangle = Triangle {...}
data Square = Square {...}
instance Shape Triangle where ...
instance Shape Square where ...
x = [Triangle (...), Square (...)]
I'm getting a compiler error because the list has different types. What's the right way to do what I'm trying to do here? The only thing I've been able to come up with is doing something like this:
data WrappedShape = WrappedShape {
getArea :: () -> Double
, getNumVertices :: () -> Integer
}
wrap s = WrappedShape {
getArea = \ () -> area s
, getNumVertices = \ () -> vertices s
}
x = [wrap (Triangle (...)), wrap (Square (...))]
This works, but it's heavy on boilerplate, since I have to effectively define Shape twice and with differently-named members. What's the standard way to do this sort of thing?
If you just need a few different shapes, you can enumerate each shape as a constructor, here is a example:
data SomeShapes = Triangle {...}
| Square {...}
instance Shape SomeShapes where
area (Triangle x) = ...
area (Square x) = ....
now you can put them in a list, because they are same type of SomeShapes
[Triangle {...}, Square {...}]
Your wrapped type is probably the best idea.
It can be improved by noting that, in a lazy language like Haskell, the type () -> T essentially works like the plain T. You probably want to delay computation and write stuff like let f = \ () -> 1+2 which does not perform addition until the function f is called with argument (). However, let f = 1+2 already does not perform addition until f is really needed by some other expression -- this is laziness.
So, we can simply use
data WrappedShape = WrappedShape {
getArea :: Double
, getNumVertices :: Integer
}
wrap s = WrappedShape {
getArea = area s
, getNumVertices = vertices s
}
x = [wrap (Triangle (...)), wrap (Square (...))]
and forget about passing () later on: when we will access a list element, the area/vertices will be computed (whatever we need). That is print (getArea (head x)) will compute the area of the triangle.
The \ () -> ... trick is indeed needed in eager languages, but in Haskell it is an anti-pattern. Roughly, in Haskell everything has a \ () -> ... on top, roughly speaking, s o there's no need to add another one.
These is another solution to your problem, which is called an "existential type". However, this sometimes turns into an anti-pattern as well, so I do not recommend to use it lightly.
It would work as follows
data WrappedShape = forall a. Shape a => WrappedShape a
x = [WrappedShape (Triangle ...), WrappedShape (Square ...)]
exampleUsage = case head x of WrappedShape s -> area s
This is more convenient when the type class has a lots of methods, since we do not have to write a lot of fields in the wrapped type.
The main downside of this technique is that it involves more complex type machinery, for no real gain. I mean a basic list [(Double, Integer)] has the same functionality of [WrappedShape] (list of existentials), so why bother with the latter?
Luke Palmer wrote about this anti-pattern. I do not agree with that post completely, but I think he does have some good points.
I do not have a clear-cut line where I would start using existentials over the basic approach, but these factors are what I would consider:
How many methods does the type class have?
Are there any methods of the type class where the type a (the one related to the class) appears not only as an argument? E.g. a method foo :: a -> (String, a)?

How should I structure constrained parameters in Haskell?

I want to build a large schema in Haskell. The constituents take parameters and the parameters are constrained. As an example, I might decide that a circle takes one parameter called Radius, which is constrained to be non-negative. I will define the parameters globally, since each can be used by multiple constituents. There may be hundreds of parameters, and many will have long, difficult-to-type names.
I have a solution of sorts, but the parameter declarations are repetitive and I'd like to simplify them. My criteria for "simple" is just to minimize the number of times a parameter name must be typed. One part of this is to simplify the parameter definitions themselves. Another is to avoid typing parameter names when creating data objects, if possible. So, one should be able to construct a Circle without actually typing "Radius".
The code is below, followed by a few more specific questions. Thanks in advance for any help!
data Constraint = Constraint
test :: Float -> Constraint -> Bool
test _ _ = undefined
--
nonnegative :: Constraint
nonnegative = undefined
--
data Expr = Constant Float -- | Variable String | Add Parameter Parameter ...
eval (Constant x) = x
--
class Parameter a where
value :: a -> Float
constraint :: a -> Constraint
validate :: a -> Bool
validate x = test (value x) (constraint x)
-- Schema. Expecting dozens of constituents with many parameters existing
-- in complex relationships.
data Shape = Circle Radius
--
-- There may be hundreds of parameters like Radius, many with long,
-- difficult-to-type names.
data Radius = Radius Expr
instance Parameter Radius where
constraint _ = nonnegative
value (Radius r) = eval r
Can you suggest a better way to structure this code?
I think Template Haskell could be used to define a parameter (like Radius) without repeating the name. Would you recommend that approach?
Is it possible to write a default rule for value? Naively, I want to
match the pattern value (_ x), but that's not well-typed, of course.
Is there some way of accomplishing the same thing?
Is there a simpler way to associate a value with a type? For instance,
Radius has a constraint associated with the type, but it seems unnecessary to
have to construct a Radius to get its constraint. When I try to write
constraint :: Constraint, GHC complains that the type parameter a is
not used.
It sounds like you wish Haskell had the ability to declare subtypes from predicates. This can be done with smart constructors, but it's true, there is a bit of boilerplate involved. I don't think Template Haskell is such a bad idea. It wouldn't be too hard to wrap up the definition of a smart constructor into something like
subtype "Radius" 'Float [| \x -> x >= 0 |]
You said that there might be hundreds of these things, which raises a design alarm in my mind. I would be looking very hard at this point for opportunities to add more conceptual abstraction to your schema—hundreds is too many. But without more info and context all that's all I can really say: beware!

Constructive solid Geometry in functional programming

I'm implementing CSG in a haskell program.
When I did that in an OOP lahguage I was inspired by the Composite Patron.
I had an abstract class "Object", some concrete objects (Sphere, plane, etc), and a concrete class "CompositeObject" whith an operator and two pointers to Object.
To implement the CSG tree in that way in Haskell I was thinking in a recursive datatype:
data Shape = Sphere (..some types here..)
| ..other primitive objects..
| Composite Shape Op Shape
Then I define the functions over objects by pattern matching.
The problem here, is that all objects must be in this module. All objects are concentrated in a monolith.
I think that is a good idea having a typeclass for objects:
class Shape a where
intersection :: Ray -> a -> [Points]
normal :: Point -> a -> Vector
...
Now I define instances for Sphere, plane, cilinder, etc.
But what about composite objects? How can I create a type constructed from two types of a class, with the class functions depending on the constructors, or something like that?
The usual pattern is to skip the class and just make it data, like this:
data Shape = Shape
{ intersection :: Ray -> [Point]
, normal :: Point -> Vector
}
Then you would have functions like sphere that took a position and a radius and produced a Shape; or a composite object that took two Shapes and did something with them.

Whats the syntax for the coproduct (disjoint union) of types in Haskell?

consider the following
data Point=Point{x::Float,y::Float}
data Shape=Circle{centre::Point,radius::Float}
|Rectangle {uleft::Point,bRight::Point}
Here the type Shape is a coproduct of two types Circle and Rectangle. I may want to reuse the types Circle and Rectangle elsewhere. So it would be useful to do this instead:
data Point=Point{x::Float,y::Float}
data Circle=Circle{centre::Point,radius::Float}
data Rectangle=Rectangle {uleft::Point,bRight::Point}
data Shape =Circle | Rectangle
but I get a compilation error when I do this: Circle is declared twice.
Whats the correct syntax for attempting this, or this not possible?
The coproduct of types in Haskell is commonly denoted by Either:
data Either a b = Left a | Right b
type Shape = Either Circle Rectangle
-- so you have shapes as either Left c for some Circle c
-- or Right r for some Rectangle r
This works quite nicely, although for technical reasons it isn't exactly a coproduct. Another common way would be to define a type like so:
data Shape = CircleShape Circle | RectangleShape Rectangle
so that CircleShape :: Circle -> Shape and RectangleShape :: Rectangle -> Shape are your two injections.
It's wrong to say as you do in your question that the original Shape is a coproduct of types Circle and Rectangle, because the latter two aren't types. If you want to set things up so that Circle p r is both a value of type Circle and a value of type Shape, then that's really contrary to the spirit of Haskell's type system (although something similar might be possible with sufficiently many type system extensions).
This isn't directly possible, but you have a few options. In this case, I would go with a GADT indexed by a DataKind:
{-# LANGUAGE DataKinds, GADTs, KindSignatures #-}
data ShapeType = Circle | Rectangle
data Shape :: ShapeType -> * where
CircleShape :: { centre :: Point, radius :: Float } -> Shape Circle
RectangleShape { uleft :: Point, bRight :: Point } -> Shape Rectangle
Then, whenever you wan to deal with shapes in general, you just use Shape a, and if you want a rectangle or a circle specifically, you use Shape Rectangle or Shape Circle, respectively.

Ambiguous type variable 'blah' in the constraint... how to fix?

I'm trying to write a simple ray-tracer in Haskell. I wanted to define a typeclass representing the various kinds of surfaces available, with a function to determine where a ray intersects them:
{-# LANGUAGE RankNTypes #-}
data Vector = Vector Double Double Double
data Ray = Ray Vector Vector
class Surface s where
intersections :: s -> Ray -> [Vector]
-- Obviously there would be some concrete surface implementations here...
data Renderable = Renderable
{ surface :: (Surface s) => s
, otherStuff :: Int
}
getRenderableIntersections :: Renderable -> Ray -> [Vector]
getRenderableIntersections re ra = intersections (surface re) ra
However this gives me the error:
Ambiguous type variable 's' in the constraint:
'Surface'
arising from a use of 'surface'
(The actual code is more complex but I've tried to distill it to something simpler, while keeping the gist of what I'm trying to achieve).
How do I fix this? Or alternatively, given that I come from a standard OO background, what am I fundamentally doing wrong?
Please don't use existential types for this! You could, but there would be no point.
From a functional standpoint you can drop this typeclass notion of Surface entirely. A Surface is something that maps a Ray to a list of Vectors, no? So:
type Surface = Ray -> [Vector]
data Renderable = Renderable
{ surface :: Surface
, otherStuff :: Int
}
Now if you really want, you can have a ToSurface typeclass essentially as you gave:
class ToSurface a where
toSurface :: a -> Surface
But that's just for convenience and ad-hoc polymorphism. Nothing in your model requires it.
In general, there are a very few use cases for existentials, but at least 90% of the time you can substitute an existential with the functions it represents and obtain something cleaner and easier to reason about.
Also, even though it may be a tad too much for you to take in, and the issues don't exactly match, you might find useful some of Conal's writing on denotational design: http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics/
In your getRenderableIntersections function you call surface. There is no way for the interpreter to figure out what instance of the class Surface you want to use. If you have two such instances:
instance Surface SurfaceA where
-- ...
instance Surface SurfaceB where
-- ...
How can the interpreter determine the type of surface?
The way you defined Renderable means there is a function surface :: Surface s => Renderable -> s.
Try creating an instance Surface SurfaceA and asking the following type query (given a simple constructor SurfaceA):
> :t surface (Renderable SurfaceA 0) -- What's the type of the expression?
So, what type is this expression? I bet you're expecting SurfaceA. Wrong. Take the type of surface. It takes a Renderable argument and we're passing it a Renderable argument. What is left after that? Surface s => s. That's the type of that expression. We still don't know what type does s represent.
If you want the type to be SurfaceA you need to change your code so it becomes something like surface :: Surface s => Renderable s -> s. This way what s is can be determined, because it is the same s used in Renderable.
EDIT: As suggested by #mokus, you could also try the ExistentialTypes extension. It allows "hiding" away type parameters on the right side of a type declaration.
data Renderable = forall s. Surface s => Renderable
{ surface :: s
, otherStuff :: Int
}
The HaskellWiki page I linked to above even has an example very similar to what you want to do.
EDIT: (By #stusmith) - For the record, I'm including code below which compiles based on these suggestions here. However I've accepted the answer which I think shows a better way of approaching things.
{-# LANGUAGE ExistentialQuantification #-}
data Vector = Vector Double Double Double
data Ray = Ray Vector Vector
class Surface_ s where
intersections :: s -> Ray -> [Vector]
data Surface = forall s. Surface_ s => Surface s
instance Surface_ Surface where
intersections (Surface s) ra = intersections s ra
data Renderable = Renderable
{ surface :: Surface
}
getRenderableIntersections :: Renderable -> Ray -> [Vector]
getRenderableIntersections re ra = intersections (surface re) ra

Resources