Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I avoid "rightward drift" in Haskell?

When I use an imperative language I often write code like

foo (x) {
    if (x < 0) return True;
    y = getForX(x);
    if (y < 0) return True;

    return x < y;
}

That is, I check conditions off one by one, breaking out of the block as soon as possible.

I like this because it keeps the code "flat" and obeys the principle of "end weight". I consider it to be more readable.

But in Haskell I would have written that as

foo x = do
    if x < 0
        then return x
        else do
            y <- getForX x

            if y < 0
                then return True
                else return $ x < y

Which I don't like as much. I could use a monad that allows breaking out, but since I'm already using a monad I'd have to lift everything, which adds words I'd like to avoid if I can.

I suppose there's not really a perfect solution to this but does anyone have any advice?

like image 502
Owen Avatar asked Aug 10 '11 09:08

Owen


2 Answers

For your specific question: How about dangling do notation and the usage of logic?

foo x = do
  if x < 0 then return x else do
  y <- getForX x
  return $ y < 0 || x < y

Edit

Combined with what hammar said, you can even get more beautiful code:

foo x | x < 0     = return x
      | otherwise = do y <- getForX x
                       return $ y < 0 || x < y
like image 156
fuz Avatar answered Nov 11 '22 21:11

fuz


Using patterns and guards can help a lot:

foo x | x < 0 = return x
foo x = do
    y <- getForX x
    if y < 0
        then return True
        else return $ x < y

You can also introduce small helper functions in a where clause. That tends to help readability as well.

foo x | x < 0 = return x
foo x = do
    y <- getForX x
    return $ bar y
  where
    bar y | y < 0     = True
          | otherwise = x < y

(Or if the code really is as simple as this example, use logic as FUZxxl suggested).

like image 28
hammar Avatar answered Nov 11 '22 23:11

hammar