Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Propagating information backwards in a recursive call

How can I propagate information backwards in a recursive call-chain?

For example:

f :: [Int] -> [Int]
f (x:xs)
  | x `mod` 17 = ...
  | otherwise = (x + 1) : f xs
f [] = []

I want to stop the evaluation at the ..., and I also want to propagate that information back to the caller (the fact that it stopped). I tried to use return types like Maybe, but then I had to pattern-match the recursive call, and thus lost tail-call optimization, since I had to evaluate it after the call returned (note: one could easily transform the above code to TR form, but I left it like this for easier understanding).

Can you come up with a better solution that still benefits from TCO?

like image 988
Anabra Avatar asked Dec 19 '25 01:12

Anabra


1 Answers

You can always use extra parameters, and return a tuple with the accumulated results. This way you still benefit from TCO, and get the information you needed.

An example:

f :: [Int] -> Bool -> [Int] -> (Bool, [Int])
f (x:xs) l accum
  | x `mod` 17 == 0 = (False, accum)
  | otherwise       = f xs l ((x+1) : accum)
f [] l accum = (True, accum)

Or in a more elegant way:

data CheckedValue a = Valid a | Invalid a
  deriving Show

f :: [Int] -> [Int] -> CheckedValue [Int]
f (x:xs) accum
  | x `mod` 17 == 0 = Invalid accum
  | otherwise       = f xs ((x+1) : accum)
f [] accum = Valid accum

Note: These functions also reverse the list, but pay no attention to that.

like image 169
Anabra Avatar answered Dec 21 '25 00:12

Anabra



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!