I'm fairly comfortable now with the rest of the arrow machinery, but I don't get how loop works. It seems magical to me, and that's bad for my understanding. I also have trouble understanding mfix. When I look at a piece of code that uses rec
in a proc
or do
block, I get confused. With regular monadic or arrow code, I can step through the computation and keep an operational picture of what's going on in my head. When I get to rec
, I don't know what picture to keep! I get stuck, and I can't reason about such code.
The example I'm trying to grok is from Ross Paterson's paper on arrows, the one about circuits.
counter :: ArrowCircuit a => a Bool Int
counter = proc reset -> do
rec output <- returnA -< if reset then 0 else next
next <- delay 0 -< output+1
returnA -< output
I assume that if I understand this example, I'll be able to understand loop in general, and it'll go a great way towards understanding mfix. They feel essentially the same to me, but perhaps there is a subtlety I'm missing? Anyway, what I would really prize is an operational picture of such pieces of code, so I can step through them in my head like 'regular' code.
Edit: Thanks to Pigworker's answer, I have started thinking about rec and such as demands being fulfilled. Taking the counter
example, the first line of the rec block demands a value called output
. I imagine this operationally as creating a box, labelling it output
, and asking the rec block to fill that box. In order to fill that box, we feed in a value to returnA, but that value itself demands another value, called next
. In order to use this value, it must be demanded of another line in the rec block but it doesn't matter where in the rec block it is demanded, for now.
So we go to the next line, and we find the box labelled next
, and we demand that another computation fill it. Now, this computation demands our first box! So we give it the box, but it has no value inside it, so if this computation demands the contents of output
, we hit an infinite loop. Fortunately, delay takes the box, but produces a value without looking inside the box. This fills next
, which then allows us to fill output
. Now that output
is filled, when the next input of this circuit is processed, the previous output
box will have its value, ready to be demanded in order to produce the next next
, and thus the next output
.
How does that sound?
rec
works. – Hyperbatonrec
ormfix
works since it depends on which arrow/monad it is. For arrows I think a good picture in your mind is to imagine it as a feedback in a circuit. To find out how that actually works you have to look at individual instances. – Facultyfix
works? i.e.,fix f = let x = f x in x
. It's the same idea. – Staffmanfix $ (0:) . map (+1)
, then? – Staffmanfix
is basically that it does this until every input to the chain of composed functions produces the same output, which is also the least interesting valuex
such that, for the original functionf
,f x == x
. – Staffmanfix
for me. – Chi