I'm trying to learn haskell by writing a chess game, and gchi is complaining about one of the functions I'm trying to write (error message: "parse error on input ‘where’ "), which is supposed to check whether a candidate chess move is legal. I suspect there's something about the syntax of the if/then expressions that I'm screwing up. The function at this point of development is written as follows:
isLegal :: Int -> Int -> Int -> Int -> Board -> Bool
isLegal from_x from_y to_x to_y (MakeBoard board_state) =
if (piece_at_origin == Nothing)
then False
else if (piece_at_origin == Just (Piece Pawn White))
then
if not (to_y == from_y + 1)
then False
else if (x_dist /= 1 && x_dist /= 0)
then False
else if (x_dist /= 0 && piece_at_destination == Nothing)
then False
else
True
where
piece_at_origin = board_state from_x from_y
piece_at_destination = board_state to_x to_y
x_dist = abs (to_x - from_x)
y_dist = abs (to_y - from_y)
When I delete the "else if" part, I don't get any problems from ghci. I'm very new to haskell so I'm sure I'm making some basic mistake here, but I would be grateful if someone could point me to what I'm doing wrong. Thanks.
Haskell has no statements, it has only expressions. This means that an if …
always has a then
and else
, and the two subexpression (the one of the then
and the else
need to have the same type). This is compiled in some expression that will return the then
part in case the condition is True
, and the else
part otherwise.
But what you aim to do can probably be expressed simpler with guards:
isLegal :: Int -> Int -> Int -> Int -> Board -> Bool
isLegal from_x from_y to_x to_y (MakeBoard board_state)
| Nothing <- piece_at_origin = False
| Just (Piece Pawn White) <- piece_at_origin =
if not (to_y == from_y + 1)
then False
else
if (x_dist /= 1 && x_dist /= 0)
then False
else
if (x_dist /= 0 && piece_at_destination == Nothing)
then False
else True
| otherwise = True
where
piece_at_origin = board_state from_x from_y
piece_at_destination = board_state to_x to_y
x_dist = abs (to_x - from_x)
y_dist = abs (to_y - from_y)
In fact it might be better to work with a helper function that decides for each piece what to do:
isLegal :: Int -> Int -> Int -> Int -> Board -> Bool
isLegal from_x from_y to_x to_y (MakeBoard board_state)
| Just p <- piece_at_origin = go p
| otherwise = False
where
piece_at_origin = board_state from_x from_y
piece_at_destination = board_state to_x to_y
x_dist = abs (to_x - from_x)
y_dist = abs (to_y - from_y)
go (Piece Pawn White)
| to_y /= from_y + 1 = False
| x_dist /= 1 && x_dist /= 0 = False
| x_dist /= 0, Nothing <- piece_at_destination = False
| otherwise = True
-- …