17 ms·
Why Continuations Are Coming to Java
- rzzzt 7y agoApache Commons Javaflow is an early implementation of continuations: https://commons.apache.org/sandbox/commons-javaflow/tutorial.html https://commons.apache.org/sandbox/commons-javaflow/tutorial...
- mcv 7y agoI vaguely seem to recall that Rhino, Cocoon's pre-Node serverside javascript, had continuations too. That much have been around 2006 or thereabouts.
- scanr 7y agoThey were great. You could serialise them, store them in a database and then deserialise them some time later and keep going. Cheap workflow engine.
- stingraycharles 7y agoContinuations are a very old concept, and you'll be finding many programming languages have support before that. For example, Scheme had continuations (call/cc) since 1975, and I'm sure there must have been implementations before that.
- bokchoi 7y agoI remember them too. I had to dig a little to find the links I remember from the past. http://web.archive.org/web/20190127111220/https://wiki.apache.org/cocoon/RhinoWithContinuations http://web.archive.org/web/20190127111220/https://wiki.apach... From the Rhino Google group, it looks like some people are still using continuations in Rhino: https://groups.google.com/forum/#!topic/mozilla-rhino/Gfo-dOzQXCw https://groups.google.com/forum/#!topic/mozilla-rhino/Gfo-dO... https://github.com/szegedi/spring-web-jsflow https://github.com/szegedi/spring-web-jsflow
- dmix 7y agoThat Rhino js engine stuff was all written in Java too.
- darkpuma 7y agoWhat impact, if any, would this have on Clojure?
- Blackthorn 7y agoIt would basically be a better and much more seamless Pulsar which is awesome.
- didibus 7y agoI had a discussion about this with some other folks recently. Clojure already supports forms of delimited continuations. You can use core.async for example, or cloroutine. That said, it can't yield across stack frames. One problem with that is that if you use a go block for example, calling anything inside it which will block can sabotage your go thread. With Project Loom, all blocking call made within a Fiber will yield it instead of blocking the thread. That would be the big advantage, and possibly something Clojure could leverage as well, so that say making a blocking IO call even indirectly inside a go block would park instead of blocking.
- punnerud 7y ago30min in and I think: Are they now trying to make Java to work like PL/SQL, Cobol, SAP (ABAP = SQL+COBOL)..? I think it is a good idea, and is a way to bridge the gaps between the old and “new” way of writing programs. The real reason is not just scalability but traceability, that you can change code at runtime, closer connected to the database..
- kristianp 7y agoThe Project Loom webpage: https://wiki.openjdk.java.net/display/loom/Main https://wiki.openjdk.java.net/display/loom/Main
- y4mi 7y agoI find it amusing that whenever people talk about Java, they're somehow using magic to describe it. It's like Java programmers see themselves as magicians... This is especially widespread with spring boot. I've even read sentences like "It's hard to say what it does, you can only see it's effects!"
- provolone 7y agoJust avoid bloated frameworks and your problem is solved.
- cryptos 7y agoThis is not a balanced argument. Spring offers a lot of helpful things and can increase productivity. But it also has it's downsides like when something is not working because all the magic behind the scenes, making you more productive normally, just doesn't work in your case. If I look at Go code, for example, I can hardly bear all the tedious repetition and dumb code! There is hardly any magic in Go, but the downside is, that you have to write every `filter`, `map` and `reduce` by hand.
- provolone 7y agoThat's fine. Use what works for you. There's more than one way to skin a cat. It isn't particularly balanced to assume that the spring way is the only way to do things because magicians... If the OP has that problem with spring then he can use the base servlet API or another solution.
- geodel 7y agoI do exactly same as you described. Use plain servlets with embedded tomcat in application. It all works very well for me. But now I have to support multiple Spring boot projects. I can't help noticing one thing common in these projects that it is about 10% functionality and 90% of Spring turd nuggets strewn all over project repos.
- cryptos 7y agoWhat is called continuations in Java is available as "coroutines" in Kotlin, today. Both terms means more or less the same, from a programmers point of view, actually Continuations are part of Kotlins coroutines. https://kotlinlang.org/docs/reference/coroutines-overview.html https://kotlinlang.org/docs/reference/coroutines-overview.ht...
- raverbashing 7y agoIs there some special jvm/dalvik support for it?
- vlaaad 7y agoWhat is a language feature of Kotlin is available as a library in Clojure: https://github.com/clojure/core.async/ https://github.com/clojure/core.async/
- repolfx 7y agoAlmost all of Kotlin's coroutines support is also in a library. The stuff that comes from the compiler is really the smallest possible slice, required because there is syntax for it.
- avmich 7y agoInteresting that, AFAIK, computer science considers continuations a more generic mechanism than coroutines. Continuations allow continuation-passing style, creating from scratch other control flow mechanisms (like return from a subroutine), exceptions and coroutines (and maybe something else which can't be reduced to items from this list, I'm not sure). Wonder if all of that can be done with coroutines?
- vkaku 7y agoAbout time. I definitely could use a yield keyword, if someone is reading this.
- gigatexal 7y agoSo it’s like python’s yield statement aka generators?
- topspin 7y agoIt's more general than generators. You can create generators using it, and also other control flow constructs.
- billsix 7y agoYup, I did exactly that in Scheme http://billsix.github.io/bug#_make_generator http://billsix.github.io/bug#_make_generator
- ufo 7y agoIt seems that these are stackless so there is no distinction between regular functions and generator functions. You can yield from many levels deep in the call stack, without needing to use "yield from"
- twic 7y ago> I serve as a technical lead for Project Loon. That is the project that's intended to add continuations and fibers to the JDK. > However, actually, Project Loom, the goal of the project is to add continuations, fibers, and tail call elimination. I'm guessing that the project is called either Loom or Loon (and i believe it's the former), but i like the idea that there are actually two cooperating projects, each of which occasionally suspends and lets the other run.
- thaumasiotes 7y agoIt's probably Loom. It's thematically connected to "fibers" and doesn't make the immediate statement that you think your own project is doomed. Neither of those is true of Loon.
- twic 7y agoDoesn't seem to have slowed the Canadians down much: https://en.wikipedia.org/wiki/Loonie https://en.wikipedia.org/wiki/Loonie
- Tharkun 7y agoIt is indeed Loom, you know, because a loom is used to weave threads into cloth. It's possibly also a loony project, but who am I to judge.
- rileyphone 7y agoThere is a Project Loon somewhere, it's a google attempt to deliver internet through balloons.
- brighter2morrow 7y agoMore bandwidth than Comcast
- mkobit 7y agoIt is indeed Project Loom - https://openjdk.java.net/projects/loom/ https://openjdk.java.net/projects/loom/
- SeanLuke 7y agoHow does this differ from scheme-style continuations? IIRC, full continuations in the scheme sense of the term are currently impossible in Java as they require forbidden stack manipulation. Or is what's being proposed here something entirely else?
- deleted 7y ago[deleted]
- chrisseaton 7y ago> currently impossible in Java as they require forbidden stack manipulation They're changing the JVM and Java specs, so what is forbidden or not will change.
- aardvark179 7y agoWe're adding delimited one-shot continuations to the JVM. The common public interface to these is likely to be fibers rather than raw continuations, as they are much easier to work with and allow the standard library and lots of existing code to take advantage of the new features with little to no change.
- maxiepoo 7y agoBy one-shot you mean dynamically enforced, right? I.e. if you invoke it a second time it will raise an exception?
- ufo 7y agoNot exactly, since they are using a fiber API instead of one built with raw continuations. In this context, being one-shot means that there is no operation for cloning or "forking" a fiber. Each time you resume the execution of a fiber, it continues to execute until it reaches its next yield point. There is no way to go "back in time" and resume execution from a previous state of the fiber, which is what multishot coroutines or the fork system call would let you do.
- bjoli 7y ago
- waste_monk 7y agoLack of Continuations / coroutines has been a huge pain in my rump for a while now; it is fantastic that they're finally adding support for it.
- dancek 7y agoWe had a weird network issue in a production system, and because the interplay of different components was very difficult to debug I briefly investigated each component. This lead me to run Jersey (a JAX-RS implementation, ie. a REST library) with a debugger. Jersey is written in a continuation passing style. That's the only time I've seen the style outside academic discussions of Scheme. I found the code flow difficult to grok and thought continuation passing didn't fit Java very well. That may all be just due to my lack of experience, though. Granted, the alternatives to handling concurrency each have their own problems. Maybe this is a good idea; it will be interesting to see in practice.
- mbrock 7y agoHaving native access to continuation control lets you avoid writing in continuation passing style. It’s common to implement native continuation control with a compiler pass that automatically converts code into continuation passing style. (Similar to how you would transpile async/await in JavaScript.)
- aardvark179 7y agoWe're using the underlying continuations in Loom to build fibers. You don't need to pass continuations, you just write normal blocking code and the runtime takes care of yielding and scheduling. All you have to do is run things as fibers, which is pretty much like running them as threads.
- lmm 7y agoI found iteratees to be the best of all worlds: you get the performance advantages of continuation-passing style, but you can still extract the start, middle, or end of a stream pipeline as an ordinary value that makes sense to reason about and test.
- jonathanstrange 7y agoSeems like every sufficiently advanced language at one time or another turns into LISP.
- qazpot 7y agoRabbit hole goes much more deeper than that. All programs eventually turn into LISP. https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule
- pjmlp 7y ago"Invited Talk - Guy Steele" at ClojureTV. We were not out to win over the Lisp programmers; we were after the C++ programmers. We managed to drag a lot of them about halfway to Lisp.
- Blackthorn 7y agoI'm not too familiar with Scheme, but Common Lisp definitely doesn't support anything like this.
- mb720 7y agoThere's an error in the presentation's slides: The type of returnIO is "a → IO a" and not "IO a → IO a".
- pron 7y agoCorrect. That was a typo that was then copied and pasted so the mistake appears twice.
- lhotkins 7y agoIt’s really interesting how the lines between imperative and functional languages are becoming more and more blurred overtime as they’re borrowing features from one another.
- mapcars 7y ago>if a continuation gets into an infinite loop, we can ask it to preempt itself, and forcefully remove it from the CPU. This is interesting, any idea of how they can do it?
- repolfx 7y agoThat's easy. All Java threads regularly pass through "safe points" where the runtime can choose to suspend them. Think about garbage collection - the runtime must be able to reliably and regularly pause threads so their stacks can be scanned. Whether interpreted or JIT compiled, there are ways to queue a piece of code 'into' a thread that's currently running and a short time later that code will execute. Safe points are used for many different things in HotSpot. So whilst I haven't looked at the code, presumably continuations are pre-empted by forcing the host thread to a safepoint and then unmounting it. Note that forced pre-emption + a scheduler + a userspace notion of a thread gives you the tiniest core of an operating system. The next big leap in operating system design is certainly language-generic virtual machines fused with the basics of an OS.
- ScottBurson 7y agoRon Pressler!? Isn't that our very own 'pron'?
- yarg 7y agoHere's a decent video with some code included: https://www.youtube.com/watch?v=vbGbXUjlRyQ https://www.youtube.com/watch?v=vbGbXUjlRyQ. The intro music's a little weird for a dev con.
- avodonosov 7y agoThe question is not why, but when. When? Our server has long waiting http handlers, which occupy java threads while waiting, thus limiting the server throughput to the number of threads java can maintain, and I'm holding off a rewrite on async servlets for several years already to avoid complicating the code, because I hope cheap threads (fibers) will become available, but there is no timeline for Loom. When can we hope it will be released?
- digaozao 7y agoMaybe with kotlin coroutines you can rewrite the application partially. Is there a difference between that and fibers?
- avodonosov 7y agoKotlin coroutines, as well as js and c# async/await, require us to change declaration of calling function. We can call a "suspend" function only from a "suspend" function, which in turn can only be called by another "suspend" function. So if I change some of my functions to "suspend" everyone upper in the call stack should be changed to "suspend", which is not always possible because that may be a 3rd party code. That's not necesserily a deal breaker, but that's the difference. For Java there is Quasar lib, which introduces async/await through bytecode instrumentation. It's by the same authors who work on the project Loom currently. I don't use it because: a) the project web site feels a bit abandoned; I guess they concentrate on Loom now 2) I need analogues of wait/notify, sychronized, etc which are used in this code and not provided by Quasar; Quasar only provides lower-level primitives. One either need to implement such constructs on top of Quasar, or reimplement the logic. So it's not a drop-in replacement. The project Loom aims to be mostly a drop-in, in particular fibers will support synchronized, wait/notify, etc.
- deleted 7y ago[deleted]
- jarym 7y agoQuasar was created by the same guy (Ron Pressler) that is the tech lead for Project Loom. The reason Quasar feels a bit abandoned is because he's bringing the thinking right into the JVM.
- gopuseyuj 7y agoi earn $400 hourly on the internet. She has been with out artwork for five months however final month her charge emerge as $12747 really on foot on the internet for some hours. study greater on this net internet sit...www.prizebest.com
- trias 7y agoHonest question, maybe obvious: Why is the java-scheduler/ thread model so much better than the OS-level one? What does the java one do, or better what does it not do? And why can't the principles which make java-scheduling fast be applied to OS-threads?
- tpxl 7y agoThe lightweight threads(fibers) are actually not threads on the OS level, they just behave similarly. As such there are no context switches etc., which are slow.
- trias 7y agoi know, but why doesn't the OS do this too? Most programmers don't care if their "thread" is an actual thread on the machine or just a "fiber". If the benefits are this large, it should be provided by the OS in my opinion.
- TickleSteve 7y agoIf the OS were to do it, it would involve a switch to kernel-space and would then be termed an OS-level thread... Another way of putting it is that yes, Operating Systems do do it. Thats what an OS-level thread is.
- jacques_chester 7y agoOS scheduling requires you to switch contexts between different modes in the CPU. Essentially, the OS has the power to perform operations that regular code does not. Threads and processes are usually built on top of these additional permissions. Context switching is relatively expensive. The CPU needs to push a lot of application state out of the way to clear the path for the OS code to run, then after the OS is done, push the OS out of the way and retrieve the application's state and code again. Whereas a fiber remains entirely inside the application code. It never requires a context switch. But a fiber loses some of the powers of the OS: for example, it can't draw hard memory boundaries between fibers that will be enforced by the CPU. For some things you want processes, for some threads, for some fibers.
- agumonkey 7y agoanybody get this feeling that all languages are converging to a similar core set ?
- drcode 7y agoAll languages are becoming Lisp, except for the added challenge of avoiding parentheses.
- agumonkey 7y agoI'd say ml but yeah
- pjmlp 7y agoDylan did it first though.
- pantulis 7y agoModula-2 had coroutines!
- pjmlp 7y agoIndeed, but I was replying to "All languages are becoming Lisp, except for the added challenge of avoiding parentheses.". Now if we are speaking about coroutines, there are other languages even older than Modula-2, although it was probably the most mainstream one at its peak.
- cultus 7y agoEveryone hates lisp, but can't avoid reinventing it.
- eatonphil 7y agoThe bits about multi-node computation reminds me of Racket's Places [0]. Have any other mainstream languages (Racket doesn't count) tried this technique before? [0] https://docs.racket-lang.org/distributed-places/index.html https://docs.racket-lang.org/distributed-places/index.html
- dfgdghdf 7y agoIn the talk he says that continuations compose much better than monads. Can someone elaborate on this?
- jerf 7y agoIn a Java context, I'd say it's undeniably true. Haskell-style composition of monadic values depends on type system features that Java doesn't have and on no account should even be thinking about adding at this point in time. And that's ignoring the syntax mess it would be in Java today, too. In a Haskell context, since continuations are typically implemented as a monadic value, the statement would be more or less nonsensical. But I don't think that would be the context in question. Edit: noelwelsh speaks of another level of composition than I was, where one is trying to take, say, State and STM and create a new monadic value that does both those things. The analysis given there is correct as well. My point here is more syntactic and basic functioning, in that even if you pick a single monadic value type to stick with, it's not going to play well in Java anyhow. So, in conclusion, generalized monad-type code in Java is just comprehensively not really possible. Continuations, or what are being called that in modern times, however, fit into Java-type languages, as proved by several very similar languages that have had them for many years, and they work fine.
- pron 7y agoWe're trying to minimize and discourage the use of monads, not encourage it :) Having said that, monads don't compose well regardless of language.
- noelwelsh 7y agoMonads don't compose, in the sense that if you two monads of different types you can't always mash them together to create a single monad that encompasses both of the effect the two monads represent. This is not surprising as (side-)effects don't in general compose so there is no reason that monads should. Now I disagree with Ron's assertion that continuations compose better than monads. Here's why: All monads can be expressed in terms of the continuation monad. As the name suggests this is just a monadic encoding of continuations. (See https://www.schoolofhaskell.com/school/to-infinity-and-beyond/pick-of-the-week/the-mother-of-all-monads https://www.schoolofhaskell.com/school/to-infinity-and-beyon...). If your language has continuations built-in then all programs are effectively running in some "ambient" monad that can express all other monads. Just like if you have exceptions you're effectively running within an error-handling monad. So essentially rather than writing `F A` (or `F[A]` or `F<A>` or whatever syntax your language supports) to represent effectful programs all programs are implicitly running within such an effect type.
- zeveb 7y agoWow, continuations are pretty awesome, because with them you can build any control structure you like. Now if Java only had macros, so that homegrown control structures could be concise & succinct, like built-ins … It's kinda of funny how languages are slowly becoming more and more Lisp-like — even Lisp is available, still offering what it offers. As an aside: argh, in Firefox it stole the spacebar, so there's no way to scroll down the page, even if I click outside of the video. Why do web pages do that?
- exabrial 7y agoHow will ThreadLocal behave under different fiber context switches?
- pron 7y agoA fiber will appear to be a thread. You can even call Thread.currentThread and get a consistent Thread object representing the fiber. This is done so that as much existing code as possible would be able to run, unchanged, in fibers. We may (or may not), however, introduce newer, better constructs that new code will be encouraged to use.
- exabrial 7y agoOooo cool! It may be important or useful to include the 'old' contract elsewhere in the SDK.
- oddthink 7y agoDoes anyone have a good link to the state of the modern thinking on continuations? I remember from back in the Scheme R5RS era there was a conflict between call/cc and dynamic-wind. If you have a dynamic extent (like for example a try {} finally {} block that does resource allocation/deallocation), and you exit, but then jump back into the block, how far do you try to re-wind the state? You probably don't re-open files, but you would probably want to re-set any dynamic-scoped variables. I don't remember seeing this raised as an issue recently, so it must have been solved somehow, and I'm just wondering how.
- ghusbands 7y agoDynamic-wind was created to tame call/cc and exceptions, rather than being in conflict with it. It directly provides the facilities for re-re-setting things when you re-enter a left scope. However, you might want to look up delimited continuations as a different approach to similar kinds of control flow.
- oddthink 7y agoYes, thank you. I was thinking of Kent Pitman's critique that unwind-protect didn't have the right semantics when implemented with dynamic-wind, and that (in general) it seemed like fluid-let and unwind-protect wanted different sorts of continuations to implement them. (And now I need to go read more about one-shot vs multi and delimited continuations.) Someone (I forget who) had a critique about dynamic-wind itself that (IIRC) involved capturing continuations from within the before- and after-thunks of dynamic-wind, but that seems more like an edge case.
- firethief 7y agoNothing is right about call-cc: http://okmij.org/ftp/continuations/against-callcc.html http://okmij.org/ftp/continuations/against-callcc.html
- mamcx 7y agoOne thing I get learning about implementing continuations is that at the end them have huge runtime cost and complicate implementation and debugging greatly. Even do a simple interpreter with them quickly get out of control. Exist update info in how tame it?
- _delirium 7y agoOne approach, which Java is taking, is to implement a more structured form of continuation, rather than say the Scheme-style call/cc ones. A quote from the article: "To be more precise, if you're interested in the theory of continuations in the academic literature, the kind of continuation of this class implements are called one-shot multi prompt delimited continuations..."
- mamcx 7y agoThat kind of continuation don't have much info that I remember and probably I'm not the only one: https://www.reddit.com/r/ProgrammingLanguages/comments/9r2ktu/rain1_blog_are_first_class_continuations_a/ https://www.reddit.com/r/ProgrammingLanguages/comments/9r2kt... but still look interesting if somehow manage to be performant-enough and tame enough. I think continuations are best for the internals of the compiler/vm than to surface to the end user...
- nimbix 7y agoWhat ever happened with continuations in Scala? A lot of people were excited about them back in 2009, but if I google them now I still only get pages from 10 years ago. I haven't used Scala in a very long time, so I stopped paying attention to any changes in the language.
- hderms 7y agoas far as I can tell scala concurrency basically boils down to a few different subclasses that have some overlap: 1. streaming/observable focused like Monix 2. IO effect type like Cats Effect and ZIO 3. Twitter futures/Stdlib futures which are different from each other in some crucial ways
- wmfiv 7y agoProbably need to add 4. Akka.
- teilo 7y agoSo will fibers in the JVM be as fast, robust, and as cheap as processes in Erlang's BEAM?
- mruts 7y agoWill this mean that Akka can support true multitasking like Erlang? If so, that sounds amazing. Being able to write blocking code in actors is such a big win.
- Decabytes 7y agoIs tail call elimination the same as tail call optimization? I know that schemes use this to keep the stack from blowing up during recursive function calls, and that currently the JVM, and by extension Clojure is not able to handle this. I also remember reading somewhere that this wasn't possible to add/not on the road map for the JVM. Does project Loom change this?
- dan-robertson 7y agoYesish. The way I read it, tail call elimination is the guaranteed application of tail call optimisation with a well defined meaning of what a tail call is. Eg scheme requires TCE. Tail call optimisation is a transformation a compiler may choose to do to make code that looks like “return foo(...)” run faster. A compiler may choose to not do it because it might not be implemented or faster or it could make debugging harder. There is no guarantee it will happen. TCO makes some code faster. TCE allows one to write different looking programs knowing they won’t blow up the stack. This all being said I think most people mean what I have referred to as TCE when they say TCO.
- timgilbert 7y ago> Is tail call elimination the same as tail call optimization? I know that schemes use this to keep the stack from blowing up during recursive function calls, and that currently the JVM, and by extension Clojure is not able to handle this. Yes, the elimination of the tail call is the optimization. FWIW, Clojure handles this through syntax, so recursive algorithms are generally implemented with loop/recur forms that don't blow up the stack (instead of having a function literally call itself): https://clojuredocs.org/clojure.core/loop https://clojuredocs.org/clojure.core/loop
- michaelmrose 7y agoClojure does explicit tail calls with recur.
- stcredzero 7y agoBecause Java eventually implements everything that was done years ago in Smalltalk. Despite some having said it was unnecessary back then. Also, expect that a feature that was implemented in Smalltalk by a single dev as a library (this applies to continuations) will take a small team in Java. (Though the Java implementation may well be more robust and faster than the Smalltalk one from years ago.)
- cetpa 7y agoJAVA is threaded go down to the OS level and is relatively resource-expensive (RAM and context switching) so don't scale well. If you want to know more visit CETPA INFOTECH PVT LTD