In "Making Our Own Types and Typeclasses" they give the following piece of code :
data Point = Point Float Float deriving (Show)
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
surface :: Shape -> Float
surface (Circle _ r) = pi * r ^ 2
surface (Rectangle (Point x1 y1) (Point x2 y2)) = (abs $ x2 - x1) * (abs $ y2 - y1)
nudge :: Shape -> Float -> Float -> Shape
nudge (Circle (Point x y) r) a b = Circle (Point (x+a) (y+b)) r
nudge (Rectangle (Point x1 y1) (Point x2 y2)) a b = Rectangle (Point (x1+a) (y1+b)) (Point (x2+a) (y2+b))
main = do
print (surface (Circle (Point 0 0) 24))
print (nudge (Circle (Point 34 34) 10) 5 10)
As it stands the pattern matching against constructors is getting quite cluttered at the point
nudge (Rectangle (Point x1 y1) (Point x2 y2)) a b = ....
Had we defined the Shape type as :
data Shape = Circle Point Float | Rectangle Float Float Float Float deriving (Show)
Then even though we lose a bit of clarity into the nature of the type, the patten matching looks less cluttered, as can be seen below :
data Point = Point Float Float deriving (Show)
data Shape = Circle Point Float | Rectangle Float Float Float Float deriving (Show)
surface :: Shape -> Float
surface (Circle _ r) = pi * r ^ 2
surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)
nudge :: Shape -> Float -> Float -> Shape
nudge (Circle (Point x y) r) a b = Circle (Point (x+a) (y+b)) r
nudge (Rectangle x1 y1 x2 y2) a b = Rectangle (x1+a) (y1+b) (x2+a) (y2+b)
main = do
print (surface (Circle (Point 0 0) 24))
print (nudge (Circle (Point 34 34) 10) 5 10)
My question is whether it is possible to have both
Rectangle Point Point
and
Rectangle Float Float Float Float
in the same piece of code (i.e. a sort of "overloading" of value constructors), so that we can do something like :
...
surface (Rectangle (Point x1 y1) (Point x2 y2)) = (abs $ x2 - x1) * (abs $ y2 - y1)
...
nudge (Rectangle x1 y1 x2 y2) a b = Rectangle (x1+a) (y1+b) (x2+a) (y2+b)
where the "..." denotes the same as in the code above. Also are there any other tricks to make the notation a bit more compact at the "nudge (Rectangle...." point? Thanks
You could possibly use type classes to make a function behave as both Point -> Point -> Rectangle and Float -> Float -> Float -> Float -> Rectangle, but I wouldn't advocate it. It will be to much trouble for the gain. I don't think there's anyway you could make such an overloaded name usable in pattern matching anyway.
The way I see it, if you're only ever going to be using Point values by deconstructing them and operating on the raw Float values, then you're not really getting that much out of it, so you could resolve your problem by getting rid of it entirely.
But you're missing a golden opportunity to implement a function to adjust a point directly!
For starters I would make an Offset type to hold your a and b values. Then you make a function adjust :: Offset -> Point -> Point to do the combining. And then your nudge doesn't even need to understand the internal structure of a Point to do its job!
For example (Disclaimer: I haven't actually compiled this)1:
data Point = Point Float Float deriving (Show)
data Offset = Offset Float Float deriving (Show)
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
adjust :: Point -> Offset -> Point
adjust (Point x y) (Offset ox oy) = Point (x + ox) (y + oy)
nudge :: Shape -> Offset -> Shape
nudge (Circle c r) o = Circle (adjust c o) r
nudge (Rectangle p1 p2) o = Rectangle (adjust p1 o) (adjust p2 o)
And similarly there could be a whole family of operations on Point and Offset. For example offsetFrom :: Point -> Point -> Offset could be useful in your surface function. I once went overboard and used type classes to implement a family of operators (|+|, |*|, etc IIRC) which allowed various things to be combined (for example, you can add a Point and an Offset in either order to get a Point, you can add and subtract Offsets but not Points, you can multiply Offsets by scalars but not Points, etc). Not sure whether it was worth it in the end, but it made my code look like my maths a little more!
With your current code you're effectively implementing all operations on Point again every time you need them (including the same adjustment operation twice in the same equation in nudge, which is my take on why it looks quite so bad).
1 There's a certain argument to be made for making functions like adjust and nudge have signatures where the "main" thing being operated on comes last, so adjust :: Offset -> Point -> Point and nudge :: Offset -> Shape -> Shape. This can come in handy because then partially applying adjust gives you a "point transformer" with type Point -> Point, and similarly you can partially apply nudge to get a "shape transformer" with type Shape -> Shape.
This helps when you have a collection of points or shapes and you want to apply the same transformation to all of them, for example:
data Shape = Polygon [Point]
adjust :: Offset -> Point -> Point
adjust (Offset ox oy) (Point x y) = Point (x + ox) (y + oy)
nudge :: Offset -> Shape -> Shape
nudge o (Polygon ps) = Polygon (map (adjust o) ps)
And generally "transformers" with type Something -> Something are just useful things to have on your main data structures. So whenever you have a function that combines some auxiliary data with a Something to produce a new Something, it'll often turn out to be useful to put the Something as the last argument, so you have another easy source of transformer functions.
One option would be to use view patterns. Let me give you a short example:
{-# LANGUAGE ViewPatterns #-}
data Point = Point Float Float
data Shape = Circle Point Float | Rectangle Point Point
rectangleAsPoints :: Shape -> Maybe (Point,Point)
rectangleAsPoints (Rectangle a b) = Just (a,b)
rectangleAsPoints _ = Nothing
rectangleFromPoints :: Point -> Point -> Shape
rectangleFromPoints = Rectangle
rectangleAsCoords :: Shape -> Maybe (Float,Float,Float,Float)
rectangleAsCoords (Rectangle (Point x y) (Point a b)) = Just (x,y,a,b)
rectangleAsCoords _ = Nothing
rectangleFromCoords :: Float -> Float -> Float -> Float -> Shape
rectangleFromCoords a b c d = Rectangle (Point a b) (Point c d)
surface (rectangleAsPoints -> Just (Point x1 y1, Point x2 y2)) =
(abs $ x2 - x1) * (abs $ y2 - y1)
surface (Circle _ r) = pi * r ^ 2
nudge (rectangleAsCoords -> Just (x1,y1,x2,y2)) a b =
rectangleFromCoords (x1+a) (y1+b) (x2+a) (y2+b)
nudge (Circle (Point x y) r) a b = Circle (Point (x+a) (y+b)) r
For consistency's sake I implemented both views of rectangles as functions. This way the actual implementation of the Rectangle type can remain hidden.
Note how you can mix normal pattern matching and view patterns.
What you want is't possibile. For the purposes of pattern matching you could use ViewPatterns as a poor-mans replacement for multiple constructors and make a single function to ease construction:
{-# LANGUAGE ViewPatterns #-}
-- Your pattern match aid
unRectangle :: Shape -> Maybe (Float, Float, Float, Float)
unRectangle (Rectangle (Point x1 y1) (Point x2 y2)) = Just (x1,y1,x2,y2)
unRectangle _ = Nothing
-- your construction aid
rectangle :: Float -> Float -> Float -> Float -> Shape
rectangle x y u v = Rectangle (Point x y) (Point u v)
surface (unRectangle -> Just (x1,y1,x2,y2)) = (abs $ x2 - x1) * (abs $ y2 - y1)
...
nudge (unRectangle -> Just (x1,y1,x2,y2)) = rectangle (x1+a) (y1+b) (x2+a) (y2+b)
Related
I have this code
type Point = (Int, Int)
data Points
= Rectangle Point Point
| Union Points Points
| Difference Points Points
inMax :: Int -> Int -> Int
inMax a b
| a < b = b
| otherwise = a
inMin :: Int -> Int -> Int
inMin a b
| a < b = a
| otherwise = b
inPoints :: Point -> Points -> Bool
inPoints (x, y) Rectangle (x1, y1) (x2, y2) = ((inMin x1 x2) <= x && x <= (inMax x1 x2)) && ((inMin y1 y2) <= y && y <= (inMax y1 y2))
I want to know if a point is in Rectangle but i got the next error: The constructor `Rectangle' should have 2 arguments, but has been given none. Why? How I should fix it?
In order to "unpack" the Rectangle, then you write Rectange between parenthesis:
inPoints :: Point -> Points -> Bool
inPoints (x, y) (Rectangle (x1, y1) (x2, y2)) = --- …
-- ↑ parenthesis ↑
We thus perform pattern matching to obtain the points of the Rectangle. You can not use Rectange itself as a patern.
The expression inPoints (x, y) Rectangle (x1, y1) (x2, y2), no matter if it appears in a pattern left of the =, or right of it, parses as
(((inPoints (x,y))
Rectangle)
(x1,y1))
(x2,y2)
i.e. Rectangle is passed as an argument all by itself, and then (x1,x2) and (x2,y2) are passed as additional, separate arguments.
That's not completely absurd: in an expression, Rectange is just a function Point -> Point -> Points, and functions can be passed as function arguments just like any other values. For example you could define something like
unionBuilt :: (Point -> Point -> Points) -> [Point] -> Points
unionBuilt f (x₀:x₁:xs) = Union (f x₀ x₁) (unionBuilt xs)
unionBuilt f ... = ...
and that could then legitimately be called like
unionBuilt Rectangle [(1,2),(3,4)]
But in a pattern match, all constructors must be fully applied, i.e. if you pattern match on Rectangle then it must always come with two patterns for the containing points (even if you're not interested in them; then you need to use a blank pattern). This generally requires wrapping it in parentheses
So what you want is
inPoints (x,y) (Rectangle (x₁,y₁) (x₂,y₂)) = ...
Like most parentheses in Haskell expressions, these are only needed if the precedence rules don't already establish this way of parsing. In particular, if you had defined your function as an infix operator, then you would have been fine, and don't actually need any parentheses at all (except for the tuples):
(∈) :: Point -> Points -> Bool
(x, y) ∈ Rectangle (x₁, y₁) (x₂, y₂)
= inMin x₁ x₂ <= x && x <= inMax x₁ x₂
&& inMin y₁ y₂ <= y && y <= inMax y₁ y₂
This is because prefix function application, including constructors like Rectangle, always binds more tightly than infix operator application.
In the following code:
data Point = Point { x :: Int,
leftHeight :: Int,
rightHeight :: Int}
data Rectangle = Rec Point Point
rec1 = Rec p1 p2
There are several constraints when creating Rec such as:
x p1 < x p2
(leftHeight p1 ) should always be zero
(rightHeight p2 ) should always be zero
rightHeight p1 == leftHeight p2
Is there a way to enforce these constraints at type level?
Because I'm using QuickCheck to generate some sample Rec but these contraints make QuickCheck very slow to generate random samples.
edit: I've solved the slow QuickCheck issue. But anyway still curious if such constraint can be expressed in Haskell
To point out the obvious, the standard way of enforcing these constraints at type level would be to define your Rectangle differently:
data Rectangle = Rec
{ x1 :: Int
, x2 :: Int
, height :: Int
}
This enforces all constraints except "x1 < x2". You can use smart destructors to recreate the rectangle's Point "fields":
point1 :: Rectangle -> Point
point1 (Rec x1 _ h) = Point x1 0 h
point2 :: Rectangle -> Point
point2 (Rec _ x2 h) = Point x2 h 0
and define a smart constructor (which can also enforce the "x1 < x2" constraint):
rect :: Point -> Point -> Rectangle
rect (Point x1 0 h1) (Point x2 h2 0) | h1 == h2 && x1 < x2 = Rec x1 x2 h1
rect _ _ = error "bad rectangle"
Seriously, this really is the best way to enforce constraints at the type level in Haskell!
I have the user data types like
data Point = Point Float Float deriving (Show)
data Shape = Circle Point Float |
Rectangle Point Point |
Triangle Point Point Point |
Label Point Font String deriving (Show)
And database like
database :: [Shape]
database = [(Circle (Point 2 5) 5), (Circle (Point 1 4) 3), (Circle (Point 8 3) 4),
(Rectangle (Point 0 5) (Point 10 0)), (Rectangle (Point 3 5) (Point 10 0)),(Rectangle (Point 0 10) (Point 20 0)),
(Triangle (Point 1 1) (Point 2 2) (Point 3 1)), (Triangle (Point 2 5) (Point 5 8) (Point 9 1))]
So how can i get figures of specified type from this?
Declare a predicate, like
isCircle :: Shape -> Bool
isCircle (Circle _ _) = True
isCircle _ = False
Then, filter by it:
databaseCircles = filter isCircle database
However, I'd advise you to decompose your types a bit:
data Circle = Circle Point Float
data Rectangle = Rectangle Point Point
data Triangle = Triangle Point Point Point
data Label = Label Point Font String
data Shape = CircleShape Circle | RectangleShape Rectangle
| TriangleShape Triangle | LabelShape Label
So that you can have a type-safe list of circles (or rectangles, or etc.):
getMaybeCircle:: Shape -> Maybe Circle
getMaybeCircle (CircleShape c) = Just c
getMaybeCircle _ = Nothing
filterCircles :: [Shape] -> [Circle]
filterCircles = catMaybes . map getMaybeCircle
Note: catMaybes is from Data.Maybe.
A list comprehension also works
[ s | s#(Circle _ _) <- database ]
I'm trying to use different data types in a list. e.g:
data Shape = Square Int
| Circle Int
| Rectangle Int Int
| Triangle Int Int Int
deriving (Show)
shapes = [Square 5, Circle 2, Rectangle 10 5]
showShapes :: [Shape] -> [Int]
showShapes [] = []
showShapes (s:xs) = getArea (s : xs)
However I'm struggling to create the method "getArea" as I need one for each different type. I don't know a way to do this using parameter pattern matching. Is there a way to do this or am I tackling this problem the wrong way?
Edit
How would you do it using an if statement and "typeOf" function
I tried changing Shape to this:
data Shape = Square Int
| Rectangle Int Int
| Triangle Int Int Int
deriving (Show, Typeable)
But I get a compile time error!
For your simple case, just use pattern matching in getArea, but you'll have to convert your values to Doubles since the area of a circle is never going to be an integer when you have an integer radius:
getArea :: Shape -> Double
getArea (Square l) = fromIntegral $ l * l
getArea (Circle r) = pi * fromIntegral r ^ 2
getArea (Rectangle l w) = fromIntegral $ l * w
-- assuming the constructor takes the 3 side lengths
getArea (Triangle a b c) = sqrt $ p * (p - a') * (p - b') * (p - c')
where
[a', b', c'] = map fromIntegral [a, b, c]
p = (a' + b' + c') / 2
Although I don't know what you want to do in showShapes. Usually the word show in Haskell means the same thing as toString in other languages, but you're trying to apply getArea inside it. Regardless, your pattern matching for showShapes is off, you need parentheses around s:xs or you'll get a syntax error, and you can't prepend a number on front of a list of Shapes as with getArea s : xs. Instead you might be wanting to calculate the area for each shape in a list? For that you can use map:
getAreas :: [Shape] -> [Double]
getAreas shapes = map getArea shapes
Note, that you don't need to store all figures in one datatype in this case. You can use existential quantification instead:
{-# LANGUAGE ExistentialQuantification #-}
data Square = Square Int
data Circle = Circle Int
data Rectangle = Rectangle Int Int
class HasArea a where
area :: a -> Double
instance HasArea Square where
area (Square n) = fromIntegral n * fromIntegral n
instance HasArea Circle where
area (Circle r) = pi * fromIntegral r ^ 2
instance HasArea Rectangle where
area (Rectangle n m) = fromIntegral n * fromIntegral m
data Shape = forall s. HasArea s => Shape s
shapes :: [Shape]
shapes = [Shape (Square 5), Shape (Circle 2), Shape (Rectangle 10 5)]
shapeArea :: Shape -> Double
shapeArea (Shape s) = area s
main = print $ map shapeArea shapes
You can read about existential quantification here: http://en.wikibooks.org/wiki/Haskell/Existentially_quantified_types
Existential quantification itself is weaker, than generalized algebraic datatypes. You can read about them here: http://en.wikibooks.org/wiki/Haskell/GADT
data Point = Point Float Float deriving (Show)
data Line = Line Point Point deriving (Show)
onLine :: Line -> Point -> Bool
onLine (Line (Point x1 y1) (Point x2 y2)) (Point x y) = True
Is there a way not to use so many brackets ?
I recommend a tool called hlint for identifying places where you can simplify your code.
In your code /as written/, you're not using the values x1, y1, x2, y2, x or y, so you could just write:
onLine _ _ = True
However, I assume that's just a stub, and in reality you will do something with the variables. In general, if you really need to reference all those variables, then you need to write it the way you have done. However, maybe you're using a helper function that only needs the entire line value. Then you could write something like:
onLine l p = blah blah blah
-- use slope l and yIntercept l to figure out if p is on the line
slope :: Line -> Float
slope (Line (Point x1 y1) (Point x2 y2)) = (y2 - y1) / (x2 - x1)
yIntercept :: Line -> Float
yIntercept (Line (Point x1 y1) (Point x2 y2)) = blah blah blah
Alternatively, you can just use accessor functions to extract the x and y co-ordinates from points and lines, but in this case it will probably make your code messier.
Also, in Haskell I believe it's generally more efficient to use Double rather than Float.
You can sometimes avoid brackets with record notation, sometimes with $, sometimes with infix functions, and sometimes they're OK if not excessive.
Let's use record notation for points, which get heavy access for the coordinates, but we'll leave Line alone.
data Point = Point {x::Double,y::Double} deriving (Show)
data Line = Line Point Point deriving (Show)
This defines x :: Point -> Double and y :: Point -> Double.
There's no such thing as equality for floating points, but I'll go for roughly right:
accuracy = 0.000000000001
is :: Double -> Double -> Bool
is x y = abs (x - y) < accuracy
I can use this as x point1 `is` x point2 neatly avoiding the bracketed is (x point1) (x point2)
When your data structure is not so heavily nested with pattern matching, a few brackets are easy to read:
gradient :: Line -> Double
gradient (Line one two) = (y two - y one) / (x two - x one)
But we can go one level deeper without using excessive brackets because of the functions x and y.
asFunction :: Line -> (Double -> Double) -- ( ) for clarity, not necessity
asFunction l#(Line point _) = \xx -> gradient l * (xx - x point) + y point
Notice I've used l# to introduce an alias for (Line point _) to save typing on the right.
Now we can use the infix function trick to get rid of a few more brackets:
onLine :: Line -> Point -> Bool
onLine l p = l `asFunction` x p `is` y p
On the right hand side, you can use $ to get rid of brackets, but you can't use it on the left in pattern matching because it's a function f $ x = f x. For example
this (that (the other thing (something other)))
= this $ that $ the other thing $ something other
= this . that . the other thing $ something other
You can take the line and point apart within the function by defining accessors, but there is no way to do the pattern matching without the parentheses.
Another way of getting rid of the parentheses is to do the pattern matching in a number of case expressions:
onLine l p = case l of
Line p1 p2 -> case p1 of
Point x1 y1 -> case p2 of
Point x2 y2 -> case p of
Point x y -> True -- you can use x1,y1,x2,y2,x and y here
This is close to what the compiler 'translates' the patternmatches to, but of course this is not much of an improvement!
However, there are a number of ways of writing this expression that also translate to the same cascaded pattern matching; here's one:
onLine l p = let
Line (Point x1 y1) (Point x2 y2) = l
Point x y = p
in True
and here's another:
onLine l p = True where
Line (Point x1 y1) (Point x2 y2) = l
Point x y = p
The last one is pretty nice and readable, in my opinion, but the other suggestions are much better, since they'll lead to a better structured program!
(There's some stuff about irrefutable patterns that I'm glossing over, and this only works for single-constructor datatypes)