Fane Spoitoru
Fane Spoitoru

Reputation: 35

The constructor `Rectangle' should have 2 arguments, but has been given none

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?

Upvotes: 1

Views: 853

Answers (2)

leftaroundabout
leftaroundabout

Reputation: 120751

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.

Upvotes: 2

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477543

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.

Upvotes: 4

Related Questions