Single Type With Several Data Constructors vs. Several Types - haskell

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.

Related

Haskell Translation Task

I'm new to programming, and I'm having trouble solving a task.
I have to use the function. In that case I have to implement it on a triangle.
I've tried different things but I'm just getting errors and that's why I'd like to ask for help.
data Triangle = Triangle {
tP1 :: Point,
tP2 :: Point,
tP3 :: Point}
deriving (Show)
First, points and vectors are two separate concepts, and should probably be distinct types, not just two different aliases for a 2-tuple.
data Point = Pt Float Float
data Vector = V Float Float
Second, your type class seems to capture the idea of translating collections of points using the same vector. The return type should then be the same as the first argument type, not hard-coded to Point.
class Polygon p where
translatePol :: p -> VectorD -> p
Now you can start simple, and define a Polygon instance for Point. (Think of a point as a degenerate polygon.)
instance Polygon Point where
translatePol (Pt x y) (Mvector v1 v2) = Pt (x + v1) (y + v2)
This can be used to define the instance for Triangle more simply.
instance Polygon Triangle where
translatePol (MTriangle p1 p2 p3) v = MTriangle (t p1) (t p2) (t p3)
where t p = translatePol p v

Iterating over custom data types in Haskell

I have a custom data type that looks like this:
data Circle = Circle
{ radius :: Float
, xPosition :: Float
, yPosition :: Float
}
I want to be able to write a scale function that can take a given circle and change its size like this:
aCircle = Circle 1.5 1 1
scaleFn aCircle 10
The desired output for this example with scale of 10 would be:
Circle 15 10 10
How can I create a function where I can iterate over each field and multiple the values by a constant? In my actual use case I need a way to map over all the fields as there are many of them.
Scaling by a factor is generally a vector space operation. You could do the following:
{-# LANGUAGE TypeFamilies, DeriveGeneric #-}
import Data.VectorSpace
import GHC.Generics (Generic)
data Circle = Circle
{ radius :: Float
, xPosition :: Float
, yPosition :: Float
} deriving (Generic, Show)
instance AdditiveGroup Circle
instance VectorSpace Circle where
type Scalar Circle = Float
main = print $ Circle 1.5 1 1 ^* 10
(result: Circle {radius = 15.0, xPosition = 10.0, yPosition = 10.0}).
(requires vector-space >= 0.11, which has just added support for generic-derived instances.)
However I should remark that Circle as such is not really a good VectorSpace instance: adding two circles doesn't make any sense, and scaling by a negative factor gives a bogus radius. Only define such an instance if your real use case follows the actual vector space axioms.
What you really want for a type like Circle is something like diagrams' Transformable class. But I don't think there's any automatic way to derive an instance for that. In fact, since diagrams has – unfortunately IMO – switched from vector-space to linear, something like this has become considerably tougher to do even in principle.
You can use "scrap your boilerplate":
import Data.Generics
data Circle = Circle
{ radius :: Float
, xPosition :: Float
, yPosition :: Float
}
deriving (Show, Data)
circleModify :: (Float -> Float) -> Circle -> Circle
circleModify f = gmapT (mkT f)
Intuitively, above, mkT f transforms f into a function which is applicable to any type: if the argument of mkT f is a Float, then f is applied, otherwise the argument is returned as it is.
The newly constructed general function is called a "transformation": the T in mkT stands for that.
Then, gmapT applies the transformation mkT f to all the fields of the circle. Note that is a field contained, say, (Float, Bool) that float would be unaffected. Use everywhere instead of gmapT to recursively go deeper.
Note that I'm not a big fan of this approach. If for any reason you change the type of a field, that change will not trigger a type error but gmapT (mkT ...) will now simply skip over that field.
Generic programming can be convenient, but sometimes a bit too much, in that type errors can be silently transformed into unexpected results at runtime. Use with care.

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

haskell polymorphism and lists

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)]

Resources