Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to simplify nested-if using to return value in Haskell

I want to check the condition of the previous if condition to determine the next if condition is to be executed or not. Each if condition may return a value.

Edit: Sorry for that the example I provided before look a bit odd...:( This is my real example, and I want to simplify the if-then-else for goingToMove

goingToMove p routes points w h = 
                        if canMove p points
                            -- the point can be moved in the map 
                            then let r = routes ++ [p]
                                     l = remainList p points
                                in move p r l w h
                            -- the point cannot be moved in the maps
                            else []

move p routes points w h = 
            if (length routes) == 2 
                then routes
                else let one = goingToMove (tallRightCorner p) routes points w h in
                    if (null one)
                        then let two = goingToMove(tallRightBCorner p) routes points w h in
                            if (null two)
                                then let three = goingToMove (tallLeftBCorner p ) routes points w h in
                                    if (null three)
                                        then ....
                                        ...... -- until, let eight = ..
                                        else three
                                else two
                        else one 

Edit:Bad example When this thing is written in java, I may use a mutable boolean flag, and return a mutable data.

public String move (int number){
        // base case
        if (number == 0){
            return "Finished the recursion";
        }
        // general case
        else {
            String result;
            boolean isNull = false;

            if ((result = move(3)) == null){
                isNull = true;
            }
            else {
                return result;
            }

            // continue to execute the if-conditions if the previous condition failed
            if (isNull){
                if((result = move(2)) == null){
                    isNull = true;
                }
                else {
                    return result;
                }
            }

            if (isNull){
                if((result = move(1)) == null){
                    isNull = true;
                }
                else {
                    return result;
                }
            }

            return null;
        }
    }

But in Haskell, there is no mutable data, and only if-then-else condition. Then the code will looks like this, and I want to simplify this because in my real work, there are 8 levels of if-then-else which look terrible and messy....

move 0 = "Finished the recursion"
move n = 
    let one = move 3 in
    if null one
        then let two = move 2 in
            if null two
                then let three = move 1 in
                        then null
                        else three
                else two
        else one
like image 311
code4j Avatar asked Feb 06 '13 18:02

code4j


People also ask

How do you do multiple If statements in Haskell?

In Haskell, multiple lines of if will be used by separating each of the if statement with its corresponding else statement. In the above example, we have introduced multiple conditions in one function. Depending on the function inputs, it will provide us different outputs.

Can a Haskell function return nothing?

No. However, you can have functions that return a trivial value.

What is returns Haskell?

return is actually just a simple function in Haskell. It does not return something. It wraps a value into a monad. Looks like return is an overloaded function.


2 Answers

In Java if I wanted to do the following:

result = func1(arg);
if (result == null){
  result = func2(arg);
  if (result == null){
    result = func3(arg);
    if (result == null){
      result = func4(arg);
    }
  }
}
return result;

What I'm essentially doing is finding the first result from func1(args), func2(args), func3(args), func4(args) that returns non-null.

In Haskell, I'd model func1, func2, func3, and func4 as functions that returned a Maybe a value, so that they could return Nothing if they failed.

func1, func2, func3, func4 :: Int -> Maybe Result

Then I can use the <|> operator (from Control.Applicative), which has the following definition for Maybe a:

Nothing <|> x = x
x       <|> _ = x

So I can convert the above Java to

func1 arg <|> func2 arg <|> func3 arg <|> func4 arg

And due to the miracle of lazy evaluation, func2 arg is only evaluated if func1 arg returns Nothing, same as in the Java example.

like image 56
rampion Avatar answered Sep 18 '22 23:09

rampion


Apart from the nice employment of <|> that rampion gave and the similar suggestion of sclv, another common way is to use guards, and exploit laziness,

move :: Int -> Maybe String
move n
    | n == 0       = Just "Finished the recursion"
    | isJust move3 = move3
    | isJust move2 = move2
    | isJust move1 = move1
    | otherwise    = Nothing
      where
        move3 = move 3
        move2 = move 2
        move1 = move 1

Due to laziness, move i (i = 3, 2, 1) is only evaluated if it's needed.

In the given case, move 3 <|> move 2 <|> move 1 is much nicer, but in cases where the conditions require evaluating different functions with different return types, the use of guards and lazy bindings in a where clause can be the natural solution to avoid awkward nested ifs.

like image 21
Daniel Fischer Avatar answered Sep 22 '22 23:09

Daniel Fischer