haskell polymorphism and lists - haskell

Suppose I have the following:
class Shape a where
draw a :: a -> IO ()
data Rectangle = Rectangle Int Int
instance Shape Rectangle where
draw (Rectangle length width) = ...
data Circle = Circle Int Int
instance Shape Circle where
draw (Circle center radius) = ...
Is there any way for me to define a list of shapes, traverse over the list, and call the draw function on each shape? The following code won't compile because the list elements aren't all the same type:
shapes = [(Circle 5 10), (Circle 20, 30), (Rectangle 10 15)]
I know I'm thinking in an OO way and trying to apply it to Haskell, and that might not be the best approach. What would be the best Haskell approach for programs that need to deal with collections of different types of objects?

If you really do need to do this, then use an existential:
{-# LANGUAGE GADTs #-}
class IsShape a where
draw :: a -> IO ()
data Rectangle = Rectangle Int Int
instance IsShape Rectangle where
draw (Rectangle length width) = ...
data Circle = Circle Int Int
instance IsShape Circle where
draw (Circle center radius) = ...
data Shape where
Shape :: IsShape a => a -> Shape
shapes = [Shape (Circle 5 10), Shape (Circle 20 30), Shape (Rectangle 10 15)]
(I renamed your class as there would be a name clash with the datatype otherwise, and having the naming this way round seems more natural).
The advantage of this solution over the other answer involving a single datatype with different constructors is that it is open; you can define new instances of IsShape wherever you like. The advantage of the other answer is that it's more "functional", and also that the closedness may in some cases be an advantage as it means that clients know exactly what to expect.

Consider using a single type instead of separate types and a typeclass.
data Shape = Rectangle Int Int
| Circle Int Int
draw (Rectangle length width) = ...
draw (Circle center radius) = ...
shapes = [Circle 5 10, Circle 20 30, Rectangle 10 15]

One way to do it would be with vtables:
data Shape = Shape {
draw :: IO (),
etc :: ...
}
rectangle :: Int -> Int -> Shape
rectangle len width = Shape {
draw = ...,
etc = ...
}
circle :: Int -> Int -> Shape
circle center radius = Shape {
draw = ...,
etc = ...
}

As Ganesh said, you could indeed use GADTs to have more type safety. But if you don't want (or need) to, here's my take on this:
As you already know, all elements of a list need to be of the same type. It isn't very useful to have a list of elements of different types, because then your throwing away your type information.
In this case however, since you want throw away type information (you're just interested in the drawable part of the value), you would suggest to change the type of your values to something that is just drawable.
type Drawable = IO ()
shapes :: [Drawable]
shapes = [draw (Circle 5 10), draw (Circle 20 30), draw (Rectangle 10 15)]
Presumably, your actual Drawable will be something more interesting than just IO () (maybe something like: MaxWidth -> IO ()).
And also, due to lazy evaluation, the actual value won't be drawn until you force the list with something like sequence_. So you don't have to worry about side effects (but you probably already saw that from the type of shapes).
Just to be complete (and incorporate my comment into this answer): This is a more general implementation, useful if Shape has more functions:
type MaxWith = Int
class Shape a where
draw :: a -> MaxWidth -> IO ()
size :: a -> Int
type ShapeResult = (MaxWidth -> IO (), Int)
shape :: (Shape a) => a -> ShapeResult
shape x = (draw x, size x)
shapes :: [ShapeResult]
shapes = [shape (Circle 5 10), shape (Circle 20 30), shape (Rectangle 10 15)]
Here, the shape function transforms a Shape a value into a ShapeResult value, by simply calling all the functions in the Shape class. Due to laziness, none of the values are actually computed until you need them.
To be honest, I don't think I would actually use a construct like this. I would either use the Drawable-method from above, or if a more general solution is needed, use GADTs. That being said, this is a fun exercise.

How to deal with a heterogeneous list of shapes in Haskell — Abstract polymorphism with type classes:
http://pastebin.com/hL9ME7qP via #pastebin
CODE:
{-# LANGUAGE GADTs #-}
class Shape s where
area :: s -> Double
perimeter :: s -> Double
data Rectangle = Rectangle {
width :: Double,
height :: Double
} deriving Show
instance Shape Rectangle where
area rectangle = (width rectangle) * (height rectangle)
perimeter rectangle = 2 * ((width rectangle) + (height rectangle))
data Circle = Circle {
radius :: Double
} deriving Show
instance Shape Circle where
area circle = pi * (radius circle) * (radius circle)
perimeter circle = 2.0 * pi * (radius circle)
r=Rectangle 10.0 3.0
c=Circle 10.0
list=[WrapShape r,WrapShape c]
data ShapeWrapper where
WrapShape :: Shape s => s -> ShapeWrapper
getArea :: ShapeWrapper -> Double
getArea (WrapShape s) = area s
getPerimeter :: ShapeWrapper -> Double
getPerimeter (WrapShape s) = perimeter s
areas = map getArea list
perimeters = map getPerimeter list

A variant of Ganesh's solution using the existential quantification syntax instead.
{-# LANGUAGE ExistentialQuantification #-}
class IsShape a where
draw :: a -> String
data Rectangle = Rectangle Int Int
instance IsShape Rectangle where
draw (Rectangle length width) = ""
data Circle = Circle Int Int
instance IsShape Circle where
draw (Circle center radius) = ""
data Shape = forall a. (IsShape a) => Shape a
shapes = [Shape (Circle 5 10), Shape (Circle 20 30), Shape (Rectangle 10 15)]

Related

Single Type With Several Data Constructors vs. Several Types

Consider the following type which describes the structure of some 2-dimensional shapes:
data DrawingElem
= Rect Pos Size
| Circle Pos Radius
| Ellipse Pos Radius Radius
| Line Pos Pos
| Polygon [Pos]
| Polyline [Pos]
| Group [DrawingElem]
| Drawing [DrawingElem]
which make use of these definitions:
data Vec = Vec Double Double
type Pos = Vec
type Size = Vec
type Radius = Double
The last two data constructors of DrawingElem are somehow special, because they make tree-like arrangements of the other types possible.
mydrawing = Drawing [Rect (Vec 0 0) (Vec 10 10),
Group (Vec 30 40) [Circle (Vec 0 0) 90,
Line (Vec 0 0) (Vec 50 50)]]
Such a data structure should finally being transformed into a renderable SVG-String:
toSvg :: DrawingElem -> String
toSvg (Drawing xs) = "<svg>" ++ concatMap toSvg xs ++ "</svg>"
toSvg (Group xs) = "<g>" ++ concatMap toSvg xs ++ "</g>"
toSvg (Rect (Vec x y) (Vec w h)) = "<rect x='" ++ x ... "</rect>"
For this purpose, it looks to me it was necessary to wrap the different shapes inside the DrawingElem type. They must have the same type in order to be nested and finally rendered.
In some other occasions, I'd like them being different types however: Say for a function which sets the size of a rectangle (and this only makes sense for rectangles, the others don't have the notion of a size):
setSize :: Size -> Rect -> Rect
This of course does not work with the above definitions and must be:
setSize :: Size -> DrawingElem -> DrawingElem
setSize (Rect p s) = ..
setSize x = x
So I'd have to implement a wildcard that makes the function complete. However writing setSize someSize someCircle without getting a type error looks problematic to me.
So finally I'm struggling with wrapping the drawing Elements inside a type VS. letting them being different types. Both properties are needed in different situations as described above.
Does someone have an advice for this? Is is an either-or, or is there maybe a way to model it which takes advantage of both ways?
One option is to use another indirection layer, and have a precise type for each element:
data DrawingElem
= DERect Rect
| DECircle Circle
...
data Rect = Rect Pos Size
data Circle = Circle Pos Radius
toSvg :: DrawingElem -> String
...
setSize :: Size -> Rect -> Rect
...
As a minor downside here we need to pattern match both layers, e.g.
toSvg (DERect (Rect pos size)) = ...
A more advanced alternative could be using a GADT. This might be overkill for your task, though.
{-# LANGUAGE GADTs, DataKinds #-}
data ElemType = Rect | Circle | ...
data DrawingElem (t :: ElemType) where
DERect :: Pos -> Size -> DrawingElem Rect
DECircle :: Pos -> Radius -> DrawingElem Circle
...
-- this works on all element types t
toSvg :: DrawingElem t -> String
...
-- this works only on a rectangle element
setSize :: Size -> DrawingElem Rect -> DrawingElem Rect
setSize size (DERect pos _) = DERect pos size
I am unconvinced about whether you actually need this. If in doubt, stick with the simpler alternative.
However writing setSize someSize someCircle without getting a type error looks problematic to me.
That would be problematic indeed. To avoid that, I will suggest a third option: perhaps you don't actually need a rectangle-specific setSize function at all. An alternative approach would be keeping a single DrawingElem type, setting an initial size on rectangle construction (and the initial radius on circle construction, etc.) and using functions that can be implemented for all kinds of elements to adjust the size after construction, such as:
scale :: Double -> DrawingElem -> DrawingElem
scaleX :: Double -> DrawingElem -> DrawingElem
scaleY :: Double -> DrawingElem -> DrawingElem
That is very similar to how gloss handles shapes (cf. the relevant type definition and some picture manipulation functions). Another example worth mentioning is diagrams, which uses a very sophisticated model for pictures, with a plethora of types and classes involved, and yet handles operations such as scaling in a similar manner.

Creating figures in Haskell

Im trying to create datatype "Figure" in Haskell, this datatype should have multiple values:
Square (with parameter length)
Triangle (with parameter length)
Circle (with parameter radius)
Every figure should have a color as well (let's say black and white), this is how my code currently looks like but this doesn't work.
Can anyone help me?
class Figure_ a where
perimeter :: a -> Double
area :: a -> Double
data Figure = forall a. Figure_ a => Figure a
type Radius = Double
type Side = Double
type Color = String
data Circle = Circle Radius
data Triangle = Triangle Side
data Square = Square Side
instance Figure_ Circle where
perimeter (Circle r) = 2 * pi * r
area (Circle r) = pi * r * r
instance Figure_ Triangle where
perimeter (Triangle x y) = 2*(x + y)
area (Triangle x y) = x * y
instance Figure_ Square where
perimeter (Square s) = 4*s
area (Square s) = s*s
instance Figure_ Figure where
perimeter (Figure shape) = perimeter shape
For forall use:
{-# LANGUAGE ExistentialQuantification #-}
Also, your triangle is accepting two parameters, so it should be like this:
data Triangle = Triangle Side Side
But looking at the formula of your Triangle, I think you seem to be confusing it with Rectangle. Also, indentation of your code doesn't seem to be correct. Your code should look like this:
class Figure_ a where
perimeter :: a -> Double
area :: a -> Double
The same indentation rule should also be followed when you create instance of that typeclass.
Im trying to create datatype "Figure" in Haskell, this datatype should have multiple values:
Square (with parameter length)
...
Circle (with parameter radius)
I'm leaving out the triangle to avoid thinking about geometry. Anyway, the questions sounds like you might want the following:
data Figure = Square Double | ... | Circle Double
And then you can define functions like:
area :: Figure -> Double
area (Square side) = side * side
area ... = ...
area (Circle radius) = pi * radius * radius
If you only need one datatype, you don't need classes in Haskell. Haskell classes are for having more than one datatype when they all support a common interface.
There is one reason to prefer your construction with class and forall over the otherwise simpler version with just data: In your version, it is easier to add another kind of figure that was not planned for. If you feel you need this, you might want to read about the existential typeclass antipattern. But if you're just trying to represent figures in Haskell at all, I would certainly start with a simple datatype.

typeclasses, overloading and instance declaration

Having this:
data Rectangle = Rectangle Height Width
data Circle = Circle Radius
class Shape a where
area :: a -> Float
perimeter :: a -> Float
instance Shape Rectangle where
area (Rectangle h w) = h * w
perimeter (Rectangle h w) = 2*h+w*2
instance Shape Circle where
area (Circle r) = pi * r**2
perimeter (Circle r) = 2*pi*r
volumenPrism base height = (area base) * height
surfacePrism shape h = (area shape) * 2 + perimeter shape * h
Why cant I write this? a is a type so why doesn't this work?
instance (Shape a) => Eq a where
x==y = area x == area y
Obviously doing like this:
instance Eq Circle where
x==y = area x == area y
first for Circle and then for Rectangle works..but it seems not the right way.
What is it I don't get in all this?
Ty
The fundamental problem is that the type class instance resolution machinery doesn't backtrack. So if you write instance Shape a => Eq a, then whenever the compiler wants to find an Eq instance, the compiler will try to use this instance and for most types it won't work out because they aren't instances of Shape.
If you still really want to do this, you can add
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
at the top of your source file.
You can also work round some of the problems described above by also adding OverlappingInstances to the set of LANGUAGE pragmas, but you will still have a global instance for Eq that will cause significant confusion elsewhere in your program.
It's much better to just enumerate the instances you really need, even if it seems ugly. You can keep the boilerplate to a minimum with a helper function, e.g.
x `areaEq` y = area x == area y
and then
instance Eq Circle where
(==) = areaEq
etc.

Implement function overloading in Haskell

I am working on the problem for writing Haskell code similar to a C++ program.
The C++ code is:
class Rectangle
{
private:
int length;
int width;
public:
Rectangle()
{
length = 0;
width = 0;
}
Rectangle(int x)
{
length = x;
width =0;
}
Rectangle ( int x , int y)
{
length = x;
width = y;
}
};
To write similar Haskell code i made a data type Rectangle
data Rectangle = Rectangle Length Width deriving (Eq, Show , Read)
type Length = Int
type Width = Int
Then i thought of making a load function which can act as constructor. But i don't understand how to implement function overloading with different number of arguments.
Please help. Thanks.
You can use record syntax to reach that behavior.
data Rectangle = Rectangle {len :: Length, width :: Width} deriving (Eq, Show , Read)
type Length = Int
type Width = Int
rectangle = Rectangle { len = 0, width = 0 }
rectangle :: Rectangle will be your constructor here.
Now you can define some Rectangle values:
λ> let a = rectangle {len = 1}
λ> a
Rectangle {len = 1, width = 0}
While it is possible to do such overloading in Haskell, it's not considered idiomatic, and will likely lead to confusing errors later on. Instead, you should simply define functions that construct data:
point :: Rectangle
point = Rectangle 0 0
line :: Length -> Rectangle
line l = Rectangle l 0
square :: Int -> Rectangle
square a = Rectangle a a
This allows you to give clear names that describe the semantics of each overloading, rather than relying on the number and type of arguments given to disambiguate which you mean.
However, if you do want to write the overloaded version, you can do it easily with typeclasses:
class MakeRectangle a where
rectangle :: a
instance MakeRectangle Rectangle where
rectangle = Rectangle 0 0
instance MakeRectangle (Length -> Rectangle) where
rectangle l = Rectangle l 0
instance MakeRectangle (Length -> Width -> Rectangle) where
rectangle = Rectangle
You'll need {-# LANGUAGE FlexibleInstances #-} at the top of your file to compile this. A trick like this is used by the standard Text.Printf library, but I would not consider it a particularly good example of overloading in Haskell; there is almost always some structure to the overloaded value's type, whereas here its entire structure is dictated by the instance, which can get in the way of type inference; not only that, but there aren't any reasonable laws that govern instances (indeed, the type is too general to permit any).
But if you really want to do it, you can, and while it's usually a bad idea, sometimes (as in the case of printf) it's the only way to accomplish the interface you want.
To try this out in GHCi, you'll need to specify the types you're using explicitly, or it won't be able to resolve the instances:
GHCi> rectangle :: Rectangle
Rectangle 0 0
GHCi> rectangle (1 :: Length) :: Rectangle
Rectangle 1 0
GHCi> rectangle (1 :: Length) (2 :: Width) :: Rectangle
Rectangle 1 2
Isn't what you are looking for simply this:
data Rectangle = Point | Line Int | Rectangle Int Int

What is wrong with this class/instance?

I have this:
data Vector3 t = Vector3 { ax, ay, az :: t }
data Point3 t = Point3 { x, y, z :: t }
data Ray3 t = Ray3 { ori :: Point3 t, dir :: Vector3 t }
data Sphere t = Sphere { center :: Point3 t, radius :: t }
I want a Shape type class, so I did this:
class Shape s where
distance :: s -> Ray3 t -> t
distance takes a shape and a ray and computes the distance to the shape along the given ray. When I try to create an instance, it doesn't work. This is what I have so far:
instance Shape (Sphere t) where
distance (Sphere center radius) ray = -- some value of type t --
How do I create an instance of Shape? I've tried everything I can think of, and I'm getting all kind of errors.
The problem is that the type variable t in Sphere t is not the same as the one in the type signature of distance. Your current types are saying that if I have a Sphere t1, I can check it against a Ray3 t2. However, you probably want these to be the same type. To solve this, I would change the Shape class to
class Shape s where
distance :: s t -> Ray3 t -> t
Now the dependency on t is explicit, so if you write your instance as
instance Shape Sphere where
distance (Sphere center radius) ray = ...
the types should line up nicely, although you'll probably need to add in some numeric constraints on t to do any useful calculations.

Resources