On Tuesday, September 22, 2015 at 6:37:13 AM UTC-5, Francesco Bellomi wrote: > > Hi Nathan, > > I think it's an awesome project, thanks for sharing this. > > Thanks. I appreciate the feedback.
> I see that currently only full continuations are supported. Would it be > possible/feasible/easy to support delimited continuations? (ie. with ranges > different from the outermost CPS context) > > I haven't done much research into delimited continuations yet, so my understanding may be wrong. However, here's my analysis of the current situation. As currently implemented, pulley.cps uses "sort of" full continuations. I say "sort of" because continuations are implicitly limited by the trampoline. So anytime a new trampoline is introduced (i.e., you call CPS code from a non-CPS function), the continuation is limited to that trampoline. However, there is currently no way to explicitly delimit the continuation. In light of the above, it seems to me that *reset(f)* could be implemented as simply as a non-CPS function that invokes the (presumably CPS'd) function *f*. I don't think this would be the ideal implementation, but it does seem to suggest it is at least possible. I'd be interested in hearing from you (and others) what use-cases you see for delimited continuations. The overviews of the topic I've seen so far seem to neglect this entirely or only address it abstractly. A few concrete examples would be a useful "jump-start". Also, it would be interesting to have a comparison with core.async's CPS > machinery: is pulley.cps expected to be more efficient performance-wise? Is > it implemented using similar or comparable techniques? > > core.async handles continuations as a state machine. On the other hand, pulley.cps implements continuations via closures. The two representations are isomorphic, so theoretically anything you can express in one you can express in the other. However, state machines (at least as implemented in core.async) must be constructed with complete knowledge of all the possible states involved. Since *go*-blocks are implemented via macro, and macros are limited to local transformations (and analysis), continuations (state transitions) within core.async are limited to the same *go*-block. In contrast, closures can come from different functions or even namespaces. So continuations in pulley.cps can cross local boundaries. That is, even though pulley.cps transforms code blocks in isolation, closures allow these blocks to coordinate and participate in the CPS protocol. This has some practical ramifications. For example, you can't compose *go*-blocks the same way you compose regular functions. In fact, you can't call a function in a *go*-block and, within that function, suspend the *go*-block. You can of course compose core.async processes, but it has a distinct look and feel from function composition. On the other hand, functions transformed by pulley.cps compose just like regular functions — because they are functions. In the examples directory, there is an implementation of a cooperative multitasking engine. Unlike core.async, you can suspend tasks from pretty much any function. There are still some limitations, but these are dynamic as opposed to static, and have to do with what I said previously about continuations being implicitly delimited. As far as performance goes, I haven't directly compared core.async to pulley.cps code yet, but I fully expect core.async to be a hands-down winner at this point. core.async is more mature and has had more performance tuning than pulley.cps. I have done some limited benchmarking against regular clojure code. All I can say is that for compute-intensive code with tight loops in CPS code, pulley.cps performs pretty poorly. You can expect a CPS version of such code to be 1 to 2 orders of magnitude slower than a non-CPS version. Interestingly, in included benchmark.clj (which benchmarks a few ways of computing the factorial function), one of the CPS implementations actually out-performed the non-CPS equivalent implemented via *loop* on OpenJDK 7 for large n. While for large n there is less time spent in CPS code and more time doing the actual multiplication, it is an interesting result because it means there was apparently some optimization it was able to perform on the CPS code that it wasn't able to do on the *loop* verion. On OpenJDK 8, the CPS code is consistently slower in all cases (though, as expected, the slow-down decreases as n increases). > thanks, > Francesco > > > > > On Monday, September 21, 2015 at 9:24:20 PM UTC+2, Nathan Davis wrote: >> >> I'm pleased to annouce the release of verion 0.2.0 of pulley.cps >> <https://github.com/positronic-solutions/pulley.cps>. pulley.cps is a >> macro-based source-to-source transforming compiler that transforms Clojure >> code into Continuation Passing Style (CPS), as well as a supporting >> run-time library. >> >> >> The main feature of this release is the addition of exception support — >> you can now use try, throw, and catch just like you would in regular >> Clojure code. There are various other enhancements as well, mostly to >> support the exception code, as documented in the changelog. >> >> >> Nathan Davis >> >> -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.