17 ms·
What Color Is Your Function?
- malfist 12y agoI had no clue this was about async functions. I assumed it was safe/unsafe functions until I got to the part about it not being. I think that is a much stronger issue than sync/async.
- kazagistar 12y agoI was convinced it was going to be about pure vs impure (monadic) code, but then he just picked out one particular monad instance (coroutines/asynchronous operations). Any time you have side effects, you potentially have "red" code, in that it can add arbitrary restrictions on how you must use the functions.
- lmm 12y agoThese kind of distinctions happen all the time. The classic http://www.joelonsoftware.com/articles/Wrong.html http://www.joelonsoftware.com/articles/Wrong.html - one colour for html-encoded strings, one colour for plain strings. Functions that might fail with an error or functions that never fail. Logging versus non-logging. Database-accessing or not. Callable from user scripts or not. Keeping track of colour - in a way where correctness is guaranteed by the computer - is such a useful general feature that any serious modern programming language should support it. We should have easy, simple syntax for specifying colour, and should make polychromatic functions almost as easy as monochromatic ones. When you do that - when the red of "async" is just one colour among a whole rainbow that your program is already tracking - then the cost of making an explicit distinction between async and not is minimal, and the benefits - being able to track, control, and see instantly whether a function is async or not - are more than worth it.
- tel 12y agoI'm not sure if you're leading this or not, but to be clear... this is exactly a type system. (Edit: read your blog; it's clear you're leading it. Poe's Law is in effect sometimes here, sorry 'bout that!)
- JoshTriplett 12y agoIt's a good type system. Many programming languages don't support this kind of type information, notably C.
- tel 12y agoI'm always curious how far a sufficiently motivated masochist could go with C... I guess it's just C++ ;)
- lmm 12y agoYou can write very safe code by using structs (even 1-element structs) for everything. But there's no generics system and very little in the way of safe tools for making one; writing a safe "generics runtime" and macros to use it puts you halfway to writing a new language.
- proveanegative 12y ago>structs (even 1-element structs) for everything. Are there any large C projects written in this style?
- lmm 12y agoAll I'm aware of is a friend's private project of around 20KLOC.
- dllthomas 12y agoI don't recall the LOC, but I used this style at a recent position. Basically, the idea is not to use bare primitive types anywhere - instead wrapping them in something semantic. Probably the two biggest wins from this approach were distinguishing types of indexes and (in the particular project) distinguishing price from quantity. It does add a bit of boilerplate. For the indexes in particular, foo_lookup(foo, idx) is substantially safer than foo[idx.value] - but then you've gotta write the lookup functions (which may just be a macro but is still a bit of cruft). Locally, if using var.value everywhere in math is getting ugly, you can of course pull things out into temporary primitive variables - enforcing things at the boundaries between functions provides almost all of the benefit.
- deleted 12y ago[deleted]
- lmkg 12y agoI guess more generally, it's about effects. The same rant could apply to any particular effect, because they share the property of being infectious: async, unsafe (in C#, not Rust), throws (checked exceptions), IO, etc. My question is: In a language that has a first-class effect system, does the red/blue problem disappear? Does being able to generalize over effect allow you to avoid cutting the world in half, and allow you to compose effects easier?
- malka 12y agoAt first, it reminded me about const-correctness in c++.
- tel 12y agoAbsolutely, yes. For instance, here is an abstraction of code which reads and writes class Monad m => MonadTeletype m where writeLn :: String -> m () readLn :: m String Here is one which receives the current time class Monad m => MonadNow m where now :: m UTCTime And here is code which transparently combines them echoTime :: (MonadNow m, MonadTeletype m) => m () echoTime = do line <- readLn t0 <- now writeLine (show t0 ++ ": " ++ line) You then, when actually executing echoTime, have to create a monadic implementation which you prove to instantiate both MonadTeletype and MonadNow. For instance, we can always show that `echoTime` can be satisfied by the Haskell "sin bin" type, IO: instance MonadTeletype IO where writeLn = putStrLn readLn = getLine instance MonadTeletype IO where now = getCurrentTime That said, it's easy to write monadic languages like MonadTeletype and MonadNow which aren't trivially satisfied by IO. This occurs when you've imputed new meaning and language into your monad, which is really cool. IO is the "sum of all evils", but it's not terrifically expressive.
- pron 12y agoExcept that's not an effect systems because you only have one real effect - the IO type. The challenge of effect systems is to describe effects and how they interact. For example, you'll have an effect that says "a lock is obtained" and one that says "a lock is released", and if you call them both, in the right order, in the same function, then that function has an effect of "mutating something under lock".
- pka 12y ago[1] is a nice read regarding continuations (and the Cont monad), though a bit more advanced. [1] http://blog.sigfpe.com/2008/12/mother-of-all-monads.html http://blog.sigfpe.com/2008/12/mother-of-all-monads.html
- deleted 12y ago[deleted]
- aidos 12y agoFor those that haven't noticed this article is by the chap who wrote the absolutely wonderful http://gameprogrammingpatterns.com/ http://gameprogrammingpatterns.com/
- tel 12y agoWe can massively generalize this by calling "blue" "pure" and "red" "impure". The end result is essentially Haskell (but you can take it much further, too!). --- There's something horrifyingly restrictive about having merely (blue -> pure, red -> impure), though. All "blue" functions behave roughly the same (e.g., very, very nicely and compositionally) but "red" functions come in many classes: async, stateful, exception-throwing, non-deterministic, CPS'd, IO-effectful, combinations of the prior. What we want is a nice way of representing different kinds of red functions and how they all interact. What we'd also like is a nice way of composing different kinds of red functions so that the bundled piece has a sensible kind of redness too and we can keep composing. And this is exactly monads and monad transformers. There are other ways to achieve this end as well all under the general name "Effect Typing". Very cool stuff. But what I'd like to emphasize is that Java/C#/Go have not solved this larger problem. They each introduce a fixed number of "rednesses" and very specific ways that different kinds of red can be connected together. Monads are a generalizable solution. The situation is exactly the same as HOFs themselves. We've had subroutines and functions for a long time, but first-order and higher-order functions are a marked increase in power since you can now refer to these "function things" directly. Monads take the notion of "CPS Transform" and allow you to refer to it directly, to mark sections of code which use it and compose them intelligently. They allow you to invent your own kinds of redness on the fly and ensure that your custom colorations compose just as nicely as the built-in ones. If this article is even slightly interesting then you owe it to yourself to learn much more about effect typing. It'll change your world. (That and sum types, because nobody is served by a bunch of integers named Token.LEFT_BRACKET.)
- munificent 12y ago> We can massively generalize this by calling "blue" "pure" and "red" "impure". The end result is essentially Haskell (but you can take it much further, too!). Yes, I wish I'd taken the time to work more Haskell into the post but this thing has been marinating on my hard drive for months and I wanted to just get it done before the Superbowl ended and the wife and kids got home. > (That and sum types, because nobody is served by a bunch of integers named Token.LEFT_BRACKET.) What said it was an integer? ;)
- viewer5 12y agoI don't have anything of substance to add, but author, if you're reading this, I enjoyed your writing style a lot.
- munificent 12y agoThank you! I know the reader's time is precious so I try to cram as much entertainment and information in there as I can.
- otakucode 12y agoSpidermouth the Night Clown will stay with me for years.
- munificent 12y agoAt some point, the phrase "Night Clown" popped into my head and it's so deliciously evocative I've had it stuck rattling around in there since then.
- Roboprog 12y agoPositively "Stross-ian" :-) (see the exploits of a certain fictional Bob Howard, former programmer)
- ufo 12y agoHe also wrote a book. You might find it interesting. http://gameprogrammingpatterns.com/ http://gameprogrammingpatterns.com/
- echoless 12y agoNo actor-model based language has this problem, so perhaps all it comes down to is baking in the right(or even any decent) concurrency support from the start, at the language level.
- sz4kerto 12y agoOf course they have. Well, everything is nice and dandy until your actors never block. As soon as you start blocking, your async model has the same problems as threads have. And you cannot really write real-life systems without blocking actors. Ask Erlang programmers whether they have dealt with this kind of stuff.
- echoless 12y agoThe main process or any parent process can be made to not block by spawning sub-processes that handle an entire group of child processes , so I don't see how that is a problem.
- dmitrig01 12y agoInteresting allegory. Pretty much the exact same thing could be said about pure/impure functions in a language like Haskell -- where I thought this was going (until I realized it was about JS)
- z3t4 12y agoI don't get it ... But hey, it took about six month for me to figure out how to write asynchronous JavaScript ... The key is to not use anonymous functions, it will flatten out the "Christmas tree" of callbacks. And it makes it possible to read what the code does, or at least what the programmer wants the code to do. It's much better then "then", then what, but, then, why complicate things when it's actually possible to be verbose.
- andybak 12y agoFunny to consider this alongside Guido's refusal to add full anonymous functions to Python. His argument seems to be "If it's too long for a single-line lambda, then it's long enough to deserve a name"
- munificent 12y agoGuido has a different, legitimate reason to not add multi-line lambdas to Python: they are super nasty to integrate with Python's grammar. Python has a strict grammar where statements (which use indentation) contain expressions, but never vice versa. Allowing statement-body lambdas would give you an expression form that contains significant indentation that could be embedded in the middle of some larger expression, like: call(some(function(lambda: insideLambda() alsoInside() ))) # Ugh, where do these go?! Handling that in a way that's readable and easy to parse is hard, and, I think, there's no solution that would fit naturally with Python's look and feel. CoffeeScript does handle it, but I think it's one of the hairier corners of its grammar and the Python community isn't quite so gung ho about accommodating grammatical weirdness like that.
- dom96 12y agoNim also handles it and it works pretty well I think.
- cben 12y agoPython also has `yield` generator syntax which can approximate `async` syntax rather well (everything is still colored but less painful). There seems to be no concensus whether generators or promises ("futures") are better but it seems to be agreed that if you want nice syntax you should be happy with generators. The worst problem is stdlib APIs weren't designed for any async style. => gevent's monkey-patching all of stdlib is considered among the _less_ nasty solutions :-( On a deeper level, I don't think the Python community even agrees async is a very important problem. Unlike the self-selection Node enjoyed, many people work on software where mostly sync code is good enough.
- StrykerKKD 12y agoWhat about isolates in Dart? I mean isolates are isolated processes, which also can be a thread and they also can communicate with each other.
- munificent 12y agoUnfortunately: 1. Isolates can only communicate with each other using asynchronous method calls. So even though you can move some work to another isolate, you can't block waiting for it to complete, so your function still has to be red. 2. Isolates are very limited in what you can send between them, which makes then not very useful in practice for much of anything.
- StrykerKKD 12y agoBut waiting is non-blocking right? It means that we can just make one isolate's role to wait for all other isolates to complete. For me the bad part of "red" functions is that testing is harder for it or I just suck at it.
- parfamz 12y agoWhat about FRP?
- Gurkenmaster 12y agoFRP has nothing to do with this. Concurrency is only an implementation detail of FRP.
- Nilzor 12y agoHis solutions is threads? Really? Has he read no history? There are problems with threads. That's why async I/O is hot right now. Threads is a limited resource. Threads are expensive to create and dispose. Context switches are espensive. Threads must be synchronized. Threads can have race conditions. Good rant, but I didn't expect him to serve such a shallow conclusion after a solid and insightful introduction.
- nkohari 12y ago> Has he read no history? Considering the author is Bob Nystrom, one of the main engineers of Dart, I'd wager the answer is yes.
- munificent 12y ago> one of the main engineers of Dart, I'd wager the answer is yes. I should point out that I'm on Dart, but I'm pretty low on the totem pole. I just happen to write more publicly than many of my teammates. :)
- sz4kerto 12y ago> That's why async I/O is hot right now Well, async I/O is good. However, the reason why it's super hot right now on the internetz is because people don't understand that race conditions and synchronization is something you _cannot avoid_. Your program either needs coordination or not. If it does, then there will be some kind of synchronization used. Dependencies and synchronization is not something that only occurs when accessing shared collections. In any condition, where the semantics of the algorithm requires ordering and/or transactionality and that algorithm is somehow distributed(between threads, processes, machines, you name it) and concurrent, then you will end up using lock/barrier/etc. semantics. No language itself will solve this issue as it stems from the problem you're trying to solve, not from the language constructs. Also, on a side note: I love what Clojure does with transactions and STM, but it essentially converts a problem into something that is easier to handle in 99% of the cases, and hard to handle in the rest. (Transaction rollbacks, etc.) Anyway: every single programmer needs to be comfortable with thread-level concurrency, as you'll face the same problems in on higher abstraction levels where it'll be more difficult to recognize them. He's not saying threads are the solution: he's saying that these 'modern' approaches don't make your life as easy as you would think. Abstracted coordination makes simple programs simpler, but if you don't understand the underlying principles then suddenly your ocean of callbacks will fail somewhere, and you won't understand why.
- fixermark 12y agoSadly, you can build a Java API to introduce callback hell if you want to. https://developer.android.com/reference/android/hardware/camera2/package-summary.html https://developer.android.com/reference/android/hardware/cam... It's nice you don't have to, though.
- anonymoushn 12y agoI don't really consider this solved in languages or runtimes that lack green threads. If you want to make 300,000 threads in Lua or Go, go right ahead, but if you port that application to Java you're going to have a bad time. An orthogonal useful thing that is sometimes not solved in languages with green threads is the ability to copy continuations. If you have call/cc or coroutine.clone, you can e.g. use rollback netcode in your fighting game and store state in execution states, but if you cannot copy execution states, you will have to choose one or the other.
- markc 12y ago>If you want to make 300,000 threads in Lua or Go, go right ahead, but if you port that application to Java you're going to have a bad time. In Java itself perhaps, but with Java you can use lightweight threads via Quasar. http://blog.paralleluniverse.co/2013/05/02/quasar-pulsar/ http://blog.paralleluniverse.co/2013/05/02/quasar-pulsar/ "a single machine can handle millions of them"
- jenius 12y agoSo I've been writing javascript full time for a couple years at this point, client, server, and open source, and what I have adopted is coercing everything into promises, which I suppose would be the author's way of saying making everything red. If you have something that is not async mixed in with something that's async, you can still add it to the promise chain and it will resolve right away. If you have a library that uses callbacks or some other thing, you can just wrap it such that it now uses promises. And then of course you can always look for alternate libraries that use promises from the start and skip step as well. I've found that using promises for everything works super well. There is no confusion or doubt at all. Everything has the potential to branch into async at any time with no consequences and without complicating the flow. And an additional benefit is that rather than checking for errors after any operation you do, you choose where to check for errors. When a promise rejects, it skips everything else in the chain until it gets to a catch. So rather than running 4 async operations and doing an "if error do this" after each operation kind of deal, you can catch the error in once place and handle it once. Promises surpress the error in the promise chain until you choose to handle it, which is dangerous if you don't understand how promises work, but really useful once you do. There are really solid promise-based libraries for all common operations in node right now. When.js for general promises, composition, and coercion, rest.js for network requests, bookshelf and knex for database connection and orm stuff, etc. If you are a js developer, give them a shot! Don't get me wrong, I'm not trying to claim that this is better than any other language-level construct by any means, but if you are working in javascript, where you have to deal with javascript's limitations as a language, from experience I can say that working in an all-promises environment makes things quite pleasant.
- utunga 12y agoI agree. I think that there are two things that are missed out from the this article. 1. Programmers are lazy and will waste cycles like crazy around IO if given a chance. Core library async get's them thinking in red mode but it also get's them actually thinking in ways that allow the overall app to usefully get on with other things. Go sounds like it might be a nice exception to this but I'm not familiar enough to comment. 2. He kinda skips over the fact that threads are really really hard to work with and test (I immediately think about wasting time with race conditions that are just about impossible to reproduce or fix). Node.JS style continuation hell has the advantage of being a model that can actually be understood and made to work reliably in a way that threading, in theory can, but in practice cannot.
- totony 12y agoYou can always make a function that sync all async operations: sleep until a global variable is changed by the callback. A pain, but still not that bad.
- jakobegger 12y agoYes, I've used a similar technique myself; however I consider it only a last resort. But doesn't that require your language to have threads? Is it possible to do this in eg. Javascript?
- totony 12y agoThere is no sleep call that i know of in javascript, but you are right it depends on the language and its implementation of sleep. Edit: although you could emulate a sleep call by setting a (pediodic) timeout
- renekooi 12y agoFor node.js, there is a module: https://www.npmjs.com/package/deasync https://www.npmjs.com/package/deasync
- radiospiel 12y agoyou can't if all code is running in the same thread, as sleeping would prevent any code to run at all.
- adrusi 12y agoA lot of commentors are mentioning that this is just a specific case of effect typing. Haskell and monads have been brought up as an example of effects typing, but I'd like to present another example that more closely resemble familiar static type systems. Nim[1], at least at one point (I'm looking at the current manual and can't find it documented), had support for tagging functions with a pragma and the compiler would enforce that functions without the pragma can't call function with the pragma outside of a special block. The compiler interpreted certain pragmas like "impure" and "exception" in a special way, outputting a warning when certain language features were used inside functions marked with the pragma. The language manual shows that the compiler still at least supports these special pragmas. It's possible that it never supported custom pragmas and I'm just misremembering. Interestingly, the author dismisses promises as not a major improvement and calls async/await and generators at least a half-way solution. It turns out that the are just a simple syntactic transform that isn't powerful enough to express everything that coroutines can. Promises, on the other hand, can. Promises are actually a monad: `.then(...)` is the bind function (`>>=`). This is essentially how the IO monad in Haskell works. [1]: https://nim-lang.org/ https://nim-lang.org/
- ufo 12y ago> It turns out that the are just a simple syntactic transform that isn't powerful enough to express everything that coroutines can. Sure, but at least with async/await you can reuse control flow structures from the original language, like for loops and try-catch. With promises you need to reimplement all of those as a library.
- tel 12y agoYou can recurse and branch just fine, you just lose for/while loops.
- munificent 12y agoRecursing can build up very long promise chains if you aren't careful and also just isn't a natural way for many programmers to express many kinds of code. Even simple branching gets nasty in many cases if, for example, one branch does something async while the other does not. You also lose try/catch.
- karlheinz 12y agojs-csp enables go-like concurrency in javascript: https://github.com/ubolonton/js-csp https://github.com/ubolonton/js-csp
- dwenzek 12y agoA related way of composing code is railway oriented programming already commented on HN (1) Underneath there are monads, sure, but the author has deliberately chosen a more mechanical metaphor which may help those for whom monads sound too abstract. The post focuses more on how to handle errors that asynchrony, but it shows well the key steps to lift a blue function into red ones and to compose these constructions. (1) https://news.ycombinator.com/item?id=7887134 https://news.ycombinator.com/item?id=7887134
- aidenn0 12y agoOr you could be like scheme and make all functions red.
- mmphosis 12y agoPOLLIT CMP $C050 BNE POLLIT It's not a function, it's a procedure. It doesn't need to return anything, it produces an effect. Interrupts will break it. await until an as yet indeterminable time in the future
- gfxmonk 12y agoIt seems like a lot of people are interested in fixing this, and would be keen to see a solution. I believe StratifiedJS is precisely that solution (for JS at least), and it has existed in working form for years: http://stratifiedjs.org/ http://stratifiedjs.org/ (it's not just an experiment - it's remarkably stable). StratidiedJS completely eliminates the sync/async distinction at the syntax level, with the compiler/runtime managing continuations automatically. A `map` function in SJS works just like you would want, regardless of whether the mapping function is synchronous or not. In addition, it provides _structured_ forms of concurrency at the language level, as well as cancellation (which most async APIs don't support). Disclosure: I work on StratifiedJS, and just wish more people knew about it.
- larard 12y agoLooks like exactly what this article is talking about. I have no idea why noone else has commented on it.... How does stratifiedjs work under the hood? Does it switch out the stack?
- erjiang 12y agoIt compiles down to JavaScript that you can then run on Node or the browser. I once wrote a big long rant about the mess that JS and Node have made trying to cope with async code and got tons of comments proposing X or Y library that would "fix" the issue. Not a single person mentioned StratifiedJS. I wonder if there was some history to it that prevented it from getting momentum.[0] [0] http://notes.ericjiang.com/posts/791 http://notes.ericjiang.com/posts/791
- afri 12y agoI think there are a couple of problems: 1 - SJS effectively 'solves' the concurrency problem, but it is not a problem that is on the top of most people's mind when they write an application. To a first approximation, the concurrency problem in JS looks "solved" to people already (promises, generators, etc), and it is only when you get down to it and look at it in detail you see that SJS is actually a substantially more complete solution to the problem. 2 - Many people see it as a 'cute' solution that doesn't scale to big applications. To counter that point we've developed a complete SJS client/server framework - https://conductance.io https://conductance.io - and are writing big complicated apps on it (such as http://magic-angle.com/ http://magic-angle.com/ ). It's still rough around the edges, but we're pretty confident that the upcoming release (scheduled for end March) will show just how powerful the SJS paradigm is. There is a presentation on it here: http://www.infoq.com/presentations/real-time-app-stratified-javascript http://www.infoq.com/presentations/real-time-app-stratified-... Disclaimer: I work on SJS!
- overgard 12y agoI'm really out on most of the "async" stuff, after having used it. (Mostly in Node and Tornado) Remember in the early 90s when Windows and Mac OS were "cooperatively" multitasked? Which is to say, you had to explicitly yield to allow other applications to run (or risk locking up the entire system). And then it was replaced with pre-emptive multitasking, which allowed the scheduler to figure out what process deserved CPU time, while allowing the programmer not to have to think about it. You could call a blocking IO function, and the OS would just go do something else while you waited. All this "async" stuff seems like a return of cooperative multitasking, only worse. Not only do I have to explicitly yield, but now it's to some event loop that can't even properly use multiple cores, or keep a coherent stack trace. It's a nightmare to debug. It's theoretically fast... except if one request forgets to yield, it can clog up the entire thing. I guess you use multiple processes for that and a dispatcher, but at that point you've basically reinvented preemptive multitasking... badly. Threads aren't perfect, but excluding STM and actor models they definitely suck the least.
- woah 12y agoHow does one request "forget to yield"?
- deathanatos 12y agoSome of these cooperatively multithreaded implementations have "green" varieties of all your standard functions; these are greenlet-aware (that is, aware of the cooperative threading & I/O loop that's happening) functions that do things like, for example, sleep. So, you might have a my_green_library.sleep and a calls_the_os.sleep; the latter of which will yield the hardware thread directly to the OS, and block that thread completely until its done. Whereas the former will perform a sort of userland context switch, and note something to the I/O loop, and then sleep until the next event. Worse, this problem makes composition hard: you need to know the entire implementation of any function you call, in order to be aware of whether or not it will cause the calling thread to block.
- overgard 12y agoI'm mostly thinking Tornado/Python, where the async stuff happened via generators (IE, the "yield" keyword). But that meant there were large chunks of the python standard library that were basically off limits because they blocked and couldn't be used with a generator, so if you used those functions the main event loop would be stuck waiting. For node, we happen to have a server that calls into a geometric modeler (for collaborative 3d modeling). Since it's doing a lot of math, you could totally conceive that while an expensive modeling operation is running and chewing through CPU cycles, all the other sessions on the system are just waiting. That's kind of a specific use case admittedly, but with threads it wouldn't even be an issue, but with async it's a problem. I get there's ways around it (offload the work to a worker process asynchronously, for instance, which is what we're doing), but it's annoying that it's a thing I have to think about when the functionality is built into the OS.
- dvirsky 12y agoTornado made async code slightly less painful by using yield and coroutines, but you still have to run blocking methods on thread pools using futures. They abstracted it really nicely and I can now write clean code if I need an occasional blocking library in my tornado code. But after writing tons of Go over the past 2-3 years, going back to async code, even with the tornado sugar, just feels like driving a manual car after getting used to automatic. It's just redundant. I've seen better, I've written way cleaner code and got better concurrency. Promises, futures, yielded generators - they are all syntactic hacks. The only language I've used that really addresses this properly is Go (disclaimer: I haven't written any Erlang).
- dvirsky 12y agodownvoted for this? really?
- erjiang 12y agoReally, any language that has threading gives you the easy concurrency that you want. Now, if you limit your choices to "concurrency, but not with OS threads", then your pool is a lot smaller. I think Clojure (core.async), Haskell (GHC), and Rust would also give you what you're looking for.
- kmike84 12y agohttps://glyph.twistedmatrix.com/2014/02/unyielding.html https://glyph.twistedmatrix.com/2014/02/unyielding.html is a good read - there are reasons why explicit sync-async "coloring" (i.e. await/yield) is better than green threads/coroutines which author admires.
- munificent 12y agoThat's a really long post, and I dimly recall reading it a while back, but after you scrape off it saying the same thing over and over again, I think it reduces down to an uncompelling argument. It's basically, "I want context switches syntactically explicit in my code. If they aren't, reasoning about it is exponentially harder." And I think that's pretty clearly a strawman. Everything the author claims about threaded code is true of any re-entrant code, multi-threaded or not. If your function inadvertently calls a function which calls the original function recursively, you have the exact same problem. But, guess what, that just doesn't happen that often. Most code isn't re-entrant. Most state isn't shared. For code that is concurrent and does interact in interesting ways, you are going to have to reason about it carefully. Smearing "yield from" all over your code doesn't solve. In practice, you'll end up with so many "yield from" lines in your code that you're right back to "well, I guess I could context switch just about anywhere", which is the problem you were trying to avoid in the first place.
- glyph 12y agoI don't think you read the article very carefully. Specifically, what I am claiming about (shared-state) threaded code which is not true of "any re-entrant code" is the fact that you cannot tell whether threaded code is re-entrant or not without a comprehensive, combinatorial whole-program analysis. It's not feasible to know what you might be re-entrant with, because you might be re-entrant with anything. With preemptive threads you really just can't do it at all, but with green threads you have to follow every call stack all the way to the bottom, because nothing else about it the bottom of the stack tells you whether you're going to context switch or not. When you get back a Deferred (or a Promise or a Future or a thunk or whatever), then the stack-inspection can be shallow; O(1) on the depth of your stack instead of O((stack_depth) * (function_length)).
- deleted 12y ago[deleted]
- Roboprog 12y agoFunny. I thought he was talking about Java 8 for a while. We (eventually, when the rest of the stack catches up) get functors / lambdas, but: * red = instance methods. * blue = static methods, which cannot call an instance method.
- jongraehl 12y agoThat's not true. You can call an instance method from anywhere given an object. There's just no implicit 'this' in a static method.
- Roboprog 12y agoEr, what's that? I seem to have lost a receiver for your message... Once you go functional, instance methods start to look like a special klugery for currying the first argument to a function. Go and Nim (for instance) hint at this more than a little, as well, with their OOP syntax. Bundling two kinds of methods within a "class" starts to feel weird when you start using individual functions.
- Paradigma11 12y agoRelevant paper: http://www.info.ucl.ac.be/~pvr/VanRoyChapter.pdf http://www.info.ucl.ac.be/~pvr/VanRoyChapter.pdf Especially p34+ regarding concurrency paradigms.