Haskell Data Declarations - haskell

Codes below are from Programming in Haskell by Hutton (p.101).
data Shape = Circle Float | Rect Float Float
square :: Float -> Shape
square n = Rect n n
area : Shape -> Float
area(Rect x y) = x * y
In ghci, if I type area(Rect 3 5), I get 15.
But if I type square 5(thinking that I would get Rect 5 5 as a result), I get an error message:
"No instance for (Show Shape) arising from a use of ‘print’
In a stmt of an interactive GHCi command: print it".
Why is that?

Behind the scenes, GHCi is trying to call print (square 5). Unfortunately this requires Shape to implement something called the Show typeclass. You can make the error go away by adding deriving Show to the end of the data Shape = Circle Float | Rect Float Float deriving Show.
There's a great section on the Show typeclass in Learn You a Haskell and a great answer on deriving in Stack Overflow.

Related

Haskell Task Instance

I saw this exercise in a book and I am trying to do it but can't get any further.
What I'm trying to do is implement, for the data type, a function
area_t :: p -> Double
that returns the area of a general triangle.
The data type Triangle defines the function "area_t".
My current code:
data Triangle = MTriangle {
tP1 :: Point,
tP2 :: Point,
tP3 :: Point}
class Polygon p where
area_t :: p -> Float
instance Polygon Point where
instance Polygon Triangle where
area_t
Error :
Couldn't match expected type ‘Float’
with actual type ‘Point -> Point -> Float -> Point’
• The equation(s) for ‘area_t’ have three arguments,
but its type ‘Point -> Float’ has only one
The area of a point is 0, so the instance for Polygon Point (if you consider points to be polygons at all), should be:
instance Polygon Point where
area_t _ = 0
Then the code you wrote for the area of a triangle seems alright, but there's two problems:
You are pattern matching on three separate points instead of a triangle
You are producing a point instead of a plain float
A working instance might look like this:
instance Polygon Triangle where
area_t (MTriangle (MPoint x1 y1) (MPoint x2 y2) (MPoint x3 y3))
= ((x2-x1)*(y3-y1) - (x3-x1)*(y2-y1))/2

Can't define function on user-defined data-type

I'm following a tutorial and have the following type definitions:
data Point = Point Float Float deriving (Show)
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
Loading the file with these definitions into ghci, and wanting to define a function, using
surface :: Shape -> Float
I get the error message Variable not in scope: surface :: Shape -> Float.
I don't even see where I do have a variable, let alone why I get this error! Any help highly appreciated.
GHCi finds the type signature for surface but can't parse the file because there's no body for that function. In Haskell functions cannot be declared but undefined.
If you still need to load your file you can add surface = undefined
data Point = Point Float Float deriving (Show)
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
surface :: Shape -> Float
surface = undefined
Use multiline input in ghci
Prelude> :{
Prelude| surface :: Shape -> Float
Prelude| surface = undefined
Prelude| :}

Algebraic Types - Haskell

I am working around with algebraic types in Haskell, doing some exercises from a worksheet. I got the following exercises:
Define an algebraic type Point for representing (the coordinates of) points in twodimensional space.
My code for this exercise:
data Point = Point Float Float
deriving (Show)
Using Point, define a modified version PositionedShape of the Shape data type which
includes the centre point of a shape, in addition to its dimensions.
Shape data previously defined:
data Shape = Circle Float |
Rectangle Float Float
deriving (Show)
My code for this exercise:
data PositionedShape = PositionedShape Shape Point
deriving (Show)
Now my question comes in this one:
Define a function:
haskell move :: PositionedShape -> Float -> Float -> PositionedShape
which moves a shape by the given x and y distances
My implementation for this was the following:
move :: PositionedShape -> Float -> Float -> PositionedShape
move (Shape (Point x y)) newX newY = Shape (Point newX newY)
This is returning me this error:
Week8.hs:103:7: error: Not in scope: data constructor ‘Shape’
Failed, modules loaded: none.
Can someone please explain me why this error and how can I solve it? I am getting a bit confused with algebraic types, I tried a lot of things but it just seems I can't get a solution.
You need to pattern match on data constructors (like Circle and Rectangle), not on type constructors as you're trying to do now (like Shape). For PositionedShape, they happen to have the same name, although you completely forgot the match on this one (and in fact, you don't need to care about the inner Shape at all except to copy it through). Also, move is meant to move the shape by the given distances, not to move it to a new given position;.
You need to use the PositionedShape constructor to break apart a PositionedShape You have used the Shape constructor instead.
Try starting with:
move (PositionedShape shape (Point old_x old_y)) [...]
How about
move (PointedShape s (Point x y)) dx dy = PointedShape s (Point (x+dx) (y+dy))

How to make function useable only for a certain data constructor of an ADT?

I'm currently playing around with ADTs in Haskell and try to build an ADT Figure:
data Figure = Rect { x :: Integer, y :: Integer, width :: Integer, height :: Integer}
| Circle { x :: Integer, y :: Integer, radius :: Integer}
| CombiFigure Figure Figure
deriving (Eq, Show, Read)
Now I came across the question how to implement a function that should not accept every Figure, but e.g. only a Circle.
Do I already have a bad design? Or is there some best-practice how to do this?
As example, think about a diameter function. All that came to my mind (I'm a complete beginner in Haskell) are the following two options, using undefined or Maybe:
1:
diameter :: Figure -> Integer
diameter (Circle _ _ r) = 2 * r
diameter _ = undefined
2:
diameter :: Figure -> Maybe Integer
diameter (Circle _ _ r) = Just (2 * r)
diameter _ = Nothing
Are there more preferable ways on how to accomplish that?
Thanks!
You are correct that there is something not right here. The best way of thinking about it would be to start at the function diameter and decide what it's type should ideally be. You would likely come up with
diameter :: Circle -> Integer
diameter (Circle _ _ r) = 2 * r
because diameters are only defined for circles.
This means that you will have to augment your data structure by splitting out Circle (and Rect too):
data Figure = RectFigure Rect
| CircleFigure Circle
| CombiFigure Figure Figure
deriving (Eq, Show, Read)
data Rect = Rect { rectX :: Integer, rectY :: Integer, rectWidth :: Integer, height :: Integer}
deriving (Eq, Show, Read)
data Circle = Circle { circleX :: Integer, circleY :: Integer, circleRadius :: Integer}
deriving (Eq, Show, Read)
which is nice because it is now more flexible: you can write functions that don't care what Figure they are applied to, and you can write functions that are defined on specific Figures.
Now, if we are in a higher-up function and have a reference to a Figure and we want to compute its diameter if it's a CircleFigure, then you can use pattern matching to do this.
Note: using undefined or exceptions (in pure code) is likely a code smell. It could probably be solved by rethinking your types. If you have to indicate failure, then use Maybe/Either.
Your type definition by itself (i.e data Figure = ...) is introducing partial functions. e.g. even though width is of type width :: Figure -> Integer it can only work on Rect values:
\> width $ Rect 1 2 3 4
3
\> width $ Circle 1 2 3
*** Exception: No match in record selector width
so, you already have defined functions which can work on one figure but not another (similar to diameter function in the question).
That said, a 3rd solution would be to define Circle, Rectangle etc, as separate types; then, define a Figure type class which defines the common interface of these types.
class Figure a where
area, perimeter :: a -> Double
instance Figure Circle where
area = ...
perimeter = ...
Additionally each type may have their own exclusive functions. Or, you may add more interfaces, (i.e. type classes) which cover some but not all the figure types.
An advantage of type classes is that they are easier to extend; e.g. if one wants to add, say, a Triangle type later on, he can opt-in any type class which applies to a triangle, and define an instance only for those type classes.
Whereas in the data Figure = ... approach, you need to find every function which can take a Figure as argument and make sure it will handle a Triangle as well. If you are shipping a library then you do not have access to all these functions.
>> for the reference, there was a similar recent discussion of data declaration vs type classes on haskell cafe mailing list.

Printing new types in Haskell

I'm following a tutorial to create a new type. This is my code:
data Shape = Circle Float Float Float | Rectangle Float Float Float Float
When I load the file with ghci and I type:
Circle 10 20 5
It prints this:
<interactive>:29:1:
No instance for (Show Shape) arising from a use of ‘print’
In a stmt of an interactive GHCi command: print it
How can I solve this?
The show function has type:
show :: Show a => a -> String
Which means it only works on things with a Show instances. You can make your types an instance of the Show class by either manually defining an instance or letting the compiler automatically derive one:
data Shape = Circle Float Float Float | Rectangle Float Float Float Float
deriving (Show)
or
instance Show Shape where
show (Circle a b c) = "Circle " ++ show a ++ " " ++ show b ++ " " ++ show c
show (Rectangle a b c d) = ...
I solved it typing this in the interpreter:
:s -u

Resources