5 ms·
Very interesting analysis. I'm fascinated with effect systems. In theory they have the potential to make code both simpler and more powerful, because many com
by the_duke 3y ago
Very interesting analysis.
I'm fascinated with effect systems.
In theory they have the potential to make code both simpler and more powerful, because many complex abstractions like generators, async/await, error propagation, injected context, etc can all be built on a simple extensible language primitive.
The by far nicest implementation I've seen is Koka lang [1]. Unfortunately it's bound to be a small research language.
In practice I do wonder though if that much power won't make code very confusing to read and write, and if the general programmer should be trusted with that much flexibility.
For Rust specifically, I do wonder how generic effects could slot into a language that was not designed for them upfront, without compromising the design and DX.
[1] https://koka-lang.github.io/koka/doc/index.html https://koka-lang.github.io/koka/doc/index.html
- sp33der89 3y agoYeah, the learning curve is there, but it has its benefits for software development. Personally I really am enjoying the one implemented by Unison: https://www.unison-lang.org/docs/fundamentals/abilities/using-abilities-pt1/ https://www.unison-lang.org/docs/fundamentals/abilities/usin... But Koka's was fun to play around with as well!
- bruce343434 3y agoWhat about a more imperative shape? What if you could have a closure that "goto"s into the parent function scope? Or imagine coroutines built with `swapcontext` (man 3).
- zozbot234 3y agoStackful fibers are an anti-pattern. See Gor Nishanov's review for the C++ ISO committee http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2018/p1364r0.pdf http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2018/p136... linked from https://devblogs.microsoft.com/oldnewthing/20191011-00/?p=102989 https://devblogs.microsoft.com/oldnewthing/20191011-00/?p=10... . Notice how it sums things up: > DO NOT USE FIBERS!
- gpderetta 3y agoLet's say that not everybody agrees. Also all the issues identified by Gor are due to retrofitting continuations into an existing language as a pure library without compiler help.
- anonymoushn 3y agoCan Gor Nishanov post one of his hand-made state machines that handles several nested protocols? Or does he believe that operating systems are good at scheduling work across 100,000 threads?
- bruce343434 3y agoHuh? That might be exactly how "effect handlers" are implemented.
- atoav 3y agoOn the very far end of the spectrum you will have something that may look a bit like mathematical notation. Something describing a relationship extremely concisely, but the hard thing will be figuring out what exactly follows from that concise expression. I can understand how people could see the appeal in this, but I think this doesn't necessarily make it easier to write good software.
- jacquesm 3y agoYou see something similar with CL and Clojure (but the latter to a lesser extent): these abstractions are so powerful that those that grok them are able to construct things that others have little chance of understanding without a significant investment in time (if at all). Go does more or less the opposite: it tries to avoid any kind of cleverness.
- closeparen 3y agoThere’s no fundamental objection to requiring people to master difficult concepts in programming: see the ubiquity of the LeetCode Hard interview. Why then is this such a barrier to the adoption of functional programming and sophisticated type systems?
- jacquesm 3y agoBecause there are many alternatives that at first sight seem both easier to master and more productive. This results in perceived lower initial cost of development.
- konschubert 3y agoBecause the benefit of the "smart" approach vs the "simple" approach is usually smaller than its cost. The cost being: Having to hire especially talented developers and developers spending more time reading and understanding code. And there is the hard-to-measure but insidious aversion of software developers to work on code that will require effort to understand.
- zozbot234 3y agoThing is, the "smart" approach can often be the simplest in the long term. What gets sold as "simple" to newcomers only adds technical debt which ultimately becomes even harder to survey and understand.
- mtlmtlmtlmtl 3y agoNothing I've seen in any other language comes close to CLs condition system. It makes exceptions look like stone age technology. Technically I'm sure you could do everything conditions do even in C with like setjmp/longjmp, but... good luck with that.
- zozbot234 3y ago> In practice I do wonder though if that much power won't make code very confusing to read and write, and if the general programmer should be trusted with that much flexibility. This is not for ordinary code, it's for writing support libraries and DSL's. You need this stuff so that the "general programmer" can write code that's as straightforward as possible in its proper context.
- mamcx 3y agoI read this as "we have a set of effects we can layer" instead of "we build give the user the ability of make effects"
- duped 3y ago> many complex abstractions like generators, async/await, error propagation, injected context, etc can all be built on a simple extensible language primitive. I have conflicting feelings about this. On the one hand, it feels like call/cc all over again (a "simple" language primitive that can express devilishly complex control flow) that a lot of people would call a design mistake. The fact you can implement complex abstractions with a small primitive doesn't make the complex abstractions not complex for consumers of the API. On the other hand, it's not call/cc and lets you do complex things with a decent amount of modularity. But I also don't buy into the argument that implementing such foundational primitives like control flow in library code is good (I use async Rust every day, and it sucks how much has been pawned off to the ecosystem, please don't reply to this comment telling me how wrong I am). Even if you can implement these things in the ecosystem doesn't mean you should - it leads to bifurcations in the wild where libraries become incompatible because they aren't working with the same tools. The very thing that makes programming a force multiplier is modularity and reuse, making those harder by making a language too expressive makes that language worse, imo. And all that said, the biggest knock that I have against algebraic effects is I haven't seen a compelling argument why they can't be replaced with coroutines which are well understood and don't require much magic to implement.
- ATMLOTTOBEER 3y agoYou are right. This situation played out (and still kinda is playing out) in Scala.
- vlovich123 3y agoI thought coroutines don’t solve the function coloring problem considering we do have coroutines but combinatorial explosion due to function coloring is an issue. In other words, I need a try_async_map function if I want to support a map operation that has failability and async (as I need a duplicate try_map vs map to support just failability). Whether this should be solved by a formal generic algebraic effects system or generic keywords is an open question but unless I’m mistaken this is a real problem that can’t be solved with coroutines. The article also highlights an advantage for generic algebraic effects by reducing the cost of async code by not constructing the Waker until there’s a suspension point. I don’t understand the proposals well enough to know if generic keywords would solve this too, but it’s certainly worth considering. I’m less bearish on new language mechanics if they solve a class of problems very consistently and improve efficiency. Complexity of usage is a concern vs something like generic keywords, but honestly self consistent mechanics like this might be less complex to master than generic keywords in the long run. There’s precedent here for example with macros which is a pretty complex subsystem but it’s carved out by experts providing libraries for the most part and I would expect similar behavior here where library code might be more complex in implementation but there’s no code duplication and the user code gets a simpler .map function usable in any context. There will also be plenty of copy-passable examples to learn from the std library APIs.