I'm relatively new to Haskell and I'm currently working on my first project. I've written a function that returns something based on a call to another function, that calls this function again (so it's recursive).
function :: a -> a
function a
| null (recursive call) = a
| otherwise = head (same recursive call)
How can I only make the recursive call once and use the result twice (as a condition and as part of the 'return value')?
function a
| null recCall = a
| otherwise = head recCall
where recCall = recursive call
But this is not a good approach. Avoid using head
, tail
, !!
. Generally there's something called “boolean blindness”: using boolean values means the program basically forgets the meaning of what you're basing your logic on, which means you need to re-introduce it, which makes your programs both more verbose and less reliable.
Better just use pattern matching on the recursion-result right away:
function a = case recursive call of
[] -> a
(h:_) -> h
This can also written with pattern guard syntax
function a
| (h:_) <- recursive call = h
| otherwise = a
While the formulations suggested by @leftroundabout are almost certainly superior programming style, I'd point out that you'd be hard-pressed to find an example where GHC didn't recognize the repeated calls and lift them to a single call, so from the standpoint of code efficiency -- as opposed to good programming style -- there's usually not much point in trying to eliminate this kind of duplication.
This can be important in other contexts, if you find yourself trying to choose between a straightforward way of writing something that involves some unnecessary duplication versus a really awkward or messy way that eliminates any duplication. Write whichever one makes for better source code, code that's easier to read and maintain and understand, and trust that the compiler will avoid unnecessary duplicate calls.
For example, there are far too many people -- myself included -- who are tempted to rewrite a tree-searching function:
data Tree a = Leaf | Node a (Tree a) (Tree a)
find1 :: (Ord a) => a -> Tree a -> Maybe a
find1 x (Leaf) = Nothing
find1 x (Node y l r) = case compare x y of
LT -> find1 x l
GT -> find1 x r
EQ -> Just y
with a helper to avoid unnecessarily passing the fixed parameter x
:
find2 :: (Ord a) => a -> Tree a -> Maybe a
find2 x = go
where go Leaf = Nothing
go (Node y l r) = case compare x y of
LT -> find2 x l
GT -> find2 x r
EQ -> Just y
If you think find2
is clearer than find1
, by all means write it that way. If you think you're writing more efficient code, think again. Compile with:
ghc -O2 -fforce-recomp -ddump-simpl -dsuppress-all -dsuppress-uniques Tree.hs
if you'd like to check for yourself that the core generated for find2
is:
find2 = find1
The same holds for your example. I'm hard-pressed to come up with something realistic, but consider your version and @leftroundabout's versions of the following rather pointless function:
foo1 :: a -> a
foo1 a | null (foo1 [a]) = a
| otherwise = head (foo1 [a])
foo2 :: a -> a
foo2 a = case foo2 [a] of
[] -> a
(h:_) -> h
foo3 :: a -> a
foo3 a | (h:_) <- foo3 [a] = h
| otherwise = a
Would it surprise you that the core generated for foo2
and foo3
is the following?
foo2 = foo1
foo3 = foo1
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