Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function definition with guards

Tags:

haskell

Classic way to define Haskell functions is

f1 :: String -> Int
f1 ('-' : cs) -> f1 cs + 1
f1 _ = 0

I'm kinda unsatisfied writing function name at every line. Now I usually write in the following way, using pattern guards extension and consider it more readable and modification friendly:

f2 :: String -> Int
f2 s
  | '-' : cs <- s = f2 cs + 1
  | otherwise = 0

Do you think that second example is more readable, modifiable and elegant? What about generated code? (Haven't time to see desugared output yet, sorry!). What are cons? The only I see is extension usage.

like image 614
demi Avatar asked Dec 08 '22 21:12

demi


2 Answers

Well, you could always write it like this:

f3 :: String -> Int
f3 s = case s of
           ('-' : cs) -> f3 cs + 1
           _          -> 0

Which means the same thing as the f1 version. If the function has a lengthy or otherwise hard-to-read name, and you want to match against lots of patterns, this probably would be an improvement. For your example here I'd use the conventional syntax.

There's nothing wrong with your f2 version, as such, but it seems a slightly frivolous use of a syntactic GHC extension that's not common enough to assume everyone will be familiar with it. For personal code it's not a big deal, but I'd stick with the case expression for anything you expect other people to be reading.

like image 183
C. A. McCann Avatar answered Dec 24 '22 09:12

C. A. McCann


I prefer writing function name when I am pattern matching on something as is shown in your case. I find it more readable.

I prefer using guards when I have some conditions on the function arguments, which helps avoiding if else, which I would have to use if I was to follow the first pattern.

So to answer your questions

Do you think that second example is more readable, modifiable and elegant?

No, I prefer the first one which is simple and readable. But more or less it depends on your personal taste.

What about generated code?

I dont think there will be any difference in the generated code. Both are just patternmatching.

What are cons? 

Well patternguards are useful to patternmatch instead of using let or something more cleanly.

addLookup env var1 var2
   | Just val1 <- lookup env var1
   , Just val2 <- lookup env var2
   = val1 + val2

Well the con is ofcourse you need to use an extension and also it is not Haskell98 (which you might not consider much of a con)

On the other hand for trivial pattern matching on function arguments I will just use the first method, which is simple and readable.

like image 41
Satvik Avatar answered Dec 24 '22 09:12

Satvik