I am fairly new to Haskell and trying to comprehend writing functions and if else conditions and everything else. I am trying to write a very basic function but I don't fully understand if-then-else usage. I have a maze that i represent as [[Char]]. And this function will simply look at the position x,y in the maze and returns if it's a valid position or not. (whether it's in the maze boundaries or not)
I have written this so far:
is_valid_position :: Int -> Int -> [[Char]] -> Bool
is_valid_position x y maze
if(x < 0 || y < 0)
then False
if(x >= length maze || y >= length (maze!!0))
then False
else True
This gives an error because of the 'else' usage right now. What I'm trying to write in python is like this:
def is_valid_position(maze, pos_r, pos_c):
if pos_r < 0 or pos_c < 0:
return False
if pos_r >= len(maze) or pos_c >= len(maze[0]):
return False
return True
How should I change my Haskell code? I appreciate any help.
The if-else expression requires both parts. You can nest the expressions, so something like if c1 then a else if c2 then b else c.
is_valid_position x y maze = if (x < 0 || y > 0)
then False
else if (x >= length maze | y >= length (maze !! 0)
then False
else True
However, you don't need if-else expression here, since you are working purely with Boolean values. You can simply use && and ||.
-- Instead of prohibiting any condition to be true,
-- require each negated condition to be true
is_valid_position x y maze = x >= 0
&& y >= 0
&& x < length maze
&& y < length (maze !! 0)
Guard conditions are also an option to break it up a little bit:
is_valid_position x y maze | x < 0 = False
| y < 0 = False
| x >= length maze = False
| y >= length (maze !! 0) = False
| otherwise = True
Chepner's answer is excellent. But the "true Haskell" way of doing things would be a little bit different.
First, we define
(!!?) :: [a] -> Int -> Maybe a
[] !!? _ = Nothing
(x:xs) !!? n = if n == 0
then Just x
else xs !!? n - 1
Then, we define
charAtPosition :: Int -> Int -> [[a]] -> Maybe a
charAtPosition x y maze = if x < 0 || y < 0
then Nothing else
case maze !!? x of
Nothing -> Nothing
Just column -> column !!? y
charAtPosition x y maze returns the character at position x, y if there is such a character. Otherwise, it returns 0. To check whether indices x and y are valid, we simply say
isValidPosition x y maze = isJust (charAtPosition x y maze)
(where isJust is imported from Data.Maybe).
Note that we can actually clean up charAtPosition using the power of monad notation. Haskell defines the following function:
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
w >>= f = case w of
Nothing -> Nothing
Just k -> f k
Using >>=, we can thus rewrite charAtPosition x y maze to be
charAtPosition x y maze = if x < 0 || y < 0
then Nothing
else (maze !!? x) >>= (!!? y)
Another common piece of monad notation is >>. In this case, >> is defined by
(>>) :: Maybe a -> Maybe b -> Maybe b
a >> b = case a of
Nothing -> Nothing
Just c -> b
The last piece of the puzzle is the guard function, defined in Control.Monad. In this case, the definition is
guard :: Bool -> Maybe ()
guard bool = if bool
then Just ()
else Nothing
With the pieces of the puzzle, we can write
charAtPosition x y maze = guard (x >= 0 && y >= 0) >> (maze !!? x) >>= (!!? y)
We can make this slightly nicer using do notation to get
charAtPosition x y maze = do guard (x >= 0 && y >= 0)
(maze !!? x) >>= (!!? y)
which is the final Haskell way to write charAtPosition. Informally, we can read the definition of charAtPosition x y maze as saying: first, make sure that x >= 0 and that y >= 0. Then, attempt to look up the xth element of maze. Finally, attempt to look up the yth element of the result.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With