This bit of magic works due to haskells laziness. As you might know, Haskell evaluates a value when needed, not when defined. Thus, this works if you don't need the value fed in directly, or maybe later.
rec
is implemented using the loop
function of ArrowLoop
. It is defined as followed:
class Arrow a => ArrowLoop a where
loop :: a (b,d) (c,d) -> a b c
instance ArrowLoop (->) where
loop f b = let (c,d) = f (b,d) in c
You can see: The output is just fed back as the input. It will be calculated just once, because Haskell will only evaluate d
when it's needed.
Here's an actual example of how to use the loop
combinator directly. This function calculates all the powers of it's argument:
powers = loop $ \(x,l) -> (l,x:map(*x)l)
(You could also write it like this instead: powers x = fix $ (x :) . map (*x)
)
How does it works? Well, the infinite list of powers is in the l
argument. The evaluation looks like this:
powers = loop $ \(x,l) -> (l,x:map(*x)l) ==>
powers b = let (c,d) = (\(x,l) -> (l,x:map(*x)l)) (b,d) in c ==>
powers b = let (c,d) = (d,b:map(*b)d) in d ==> -- Now we apply 2 as an argument
powers 2 = let (c,d) = (d,2:map(*2)d) in d ==>
= let (c,(2:d)) = (d,2:map(*2)d) in c ==>
= let (c,(2:4:d)) = ((2:d),2:map(*2)(2:d)) in c ==>
= let (c,(2:4:8:d)) = ((2:4:d),2:map(*2)(2:4:d)) in ==> -- and so on