Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: if-then-else conditioning problem

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.

like image 860
bibble Avatar asked May 13 '26 22:05

bibble


2 Answers

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
like image 69
chepner Avatar answered May 16 '26 21:05

chepner


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.

like image 39
Mark Saving Avatar answered May 16 '26 20:05

Mark Saving