Arc Forumnew | comments | leaders | submitlogin
1 point by akkartik 5087 days ago | link | parent

Welcome back, rocketnia! I missed you ^_^

I had a sense of what delimited continuations were; I was looking for exactly your explanation.

And the imagery of continuations as trees that grow at the leaves is really powerful.

I don't follow the final two paragraphs, but I will reflect on them and read more about dynamic-wind.



1 point by rocketnia 5086 days ago | link

"Welcome back, rocketnia! I missed you ^_^ "

Sorry for leaving you hanging. XD I've been here the whole time, but I've been a bit more of a silent lurker than usual. I tend to check Arc Forum on my phone, at times when I'm already a bit tired and not patient enough to search out all the exact quotes and links I'd want to use in a reply. Thumb-typing is not my obstacle; dealing with poor interfaces for tabbed browsing is. :-p

---

"I don't follow the final two paragraphs, but I will reflect on them and read more about dynamic-wind."

I'll elaborate--er, rant about those paragraphs anyway. >.>

---

Me: "Now, if we have 'dynamic-wind, we don't really need dynamic bindings again, 'cause we can maintain the value of a mutable box as we leave one program branch and enter another. But Racket's parameters still have an edge over this technique, because the body of 'parameterize is in tail position."

In Java (or your favorite Java-like language :-p ), when an exception is thrown, the program races to unwind the stack, making a stop at each "catch" or "finally" to see if there's something else it needs to take care of along the way, and to see if it should stop unwinding.

In Scheme, when you call a continuation, the program unwinds itself down one stack branch and winds back up onto another, making a stop at each "dynamic-wind" to do the same kinds of duties. A dynamic-wind form has a before part and an after part. (It might as well be two separate forms.) Arc exposes the after part by way of 'protect and 'after, but it leaves out the before part.

The continuation-tree imagery is particularly important with 'dynamic-wind. During a continuation jump, we don't just do every after parts in the stack, swap in the new stack, and do all the before parts. We only wind back just as far as the point where the continuations meet. (But so many languages with continuations neglect to even provide 'dynamic-wind (cough Arc, Ruby cough) that I wouldn't be surprised to see Scheme implementations that do provide it but accidentally give it nonstandard semantics.)

---

Me: "On another note, continuations aren't the only way we might consider the program's dynamic behavior to branch. So are threads. But thread-local boxes are usually called just that."

As far as thread-local boxes go, I like to think of a multithreaded program as a tree where the initial thread is the root, and each thread it spawns is a branch coming off of it. In different branches of the tree I might want different configuration settings to hold, in a similar way to what I'd achieve with dynamic boxes. In Java this can be achieved with InheritableThreadLocal, and in Racket it can be achieved by constructing a thread cell with a truthy value for its "preserved" parameter.

I don't use threads much. The people who do apparently prefer to manage thread-local state on an individual thread-by-thread basis, rather than putting them in a scope tree like this. In Java, ThreadLocal is easier to type than InheritableThreadLocal, and in Racket, (make-thread-cell null 'preserved) is easier to type than (make-thread-cell null). Maybe that makes sense: A thread isn't always merely a "part" of its creator's computation the way a stack frame is; threads can be passed around and collected into all kinds of spaghetti-like dependency combinations. Maybe that's what people tend to do with them. :-p

-----

1 point by akkartik 5086 days ago | link

And what does parameterize and being in tail position have to do with it?

-----

1 point by rocketnia 5086 days ago | link

Oh, right.

The body of a dynamic-wind isn't in tail position, because just after it determines a return value (or other exit), it runs some extra code (the after part).

The Racket documentation specifically says that the last expression in a 'parameterize form is in tail position, so they must use some magic or other. What I suspect is that for every stack frame, Racket keeps a table of the parameterizations made by that stack frame. Any given parameterize form doesn't need to introduce a new stack frame; it can just mutate the most recent stack frame's association data structure, overwriting any previous value it had been binding to that parameter. Technically it's storing something on the stack this way, but it's only storing a maximum of one thing per parameter per frame.

In fact, they vaguely spell this out if you look in the right place in the Racket docs (http://docs.racket-lang.org/reference/eval-model.html#(part....): "Each frame is conceptually annotated with a set of continuation marks . A mark consists of a key and its value[...] For example, marks can be used [...] to implement dynamic scope."

-----