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