8 ms·
While cute this is really close to call/cc and/or algebraic effects as an abstraction for control flow, which isn't as much "cute" as it is "a powerful way to e
by duped 3y ago
While cute this is really close to call/cc and/or algebraic effects as an abstraction for control flow, which isn't as much "cute" as it is "a powerful way to express programs in as few expressions as possible"
- btown 3y agoYep! Everyone should read https://overreacted.io/algebraic-effects-for-the-rest-of-us/ https://overreacted.io/algebraic-effects-for-the-rest-of-us/ for how powerful this can be in practice. The caller gets to pause execution on the raising/hurling of an event - which makes many crazy things possible.
- compscigirl 3y agoI feel like I must be missing something. It seems to me (just from reading this post) that if you need algebraic effects you’ve probably painted yourself into a corner and should probably reconsider your program’s architecture. It seems like a good way obscure what the code is really doing and end up with poorly organised spaghetti code. In the post he’s effectively making a function async while pretending it’s not, or calling a callback but making the top level code’s problem instead of encapsulating in the associated type. I’m sure there’s uses for algebraic effects down the line I haven’t thought of, but I don’t understand why these would be desired outcomes?
- dvt 3y agoYou're not missing anything. Algebraic effects are basically typed gotos†[1][2], and are generally a bad idea to use and/or implement. They can be useful in a few very niche situations when using typed functional languages. † With added asynchronicity. [1] http://community.schemewiki.org/?call-with-current-continuation http://community.schemewiki.org/?call-with-current-continuat... [2] Some argue call/cc is, in fact, worse than goto: https://okmij.org/ftp/continuations/against-callcc.html https://okmij.org/ftp/continuations/against-callcc.html
- habitue 3y agoloops are typed gotos, so are if statements, so, really are functions. Goto isn't bad, it's integral to programming. It's just too powerful, so we tame it in various ways. Algebraic effects are one of those ways.
- dvt 3y ago> loops are typed gotos, so are if statements, so, really are functions. All of the above don't break the continuity of a program's control flow. Maybe you should be arguing with Dijkstra, not me: https://www.cs.utexas.edu/users/EWD/transcriptions/EWD02xx/EWD215.html https://www.cs.utexas.edu/users/EWD/transcriptions/EWD02xx/E... Pretty sure we've already litigated that goto/jmp is bad, which is why it's almost never used in modern C/C++ code unless doing very specific things.
- Turskarama 3y agoThe big difference here is to understand the program with Algebraic effects you now need to know _where the code was called from_. If you ask for an effect to come from higher up the call stack, does that mean part of the function signature needs to include that the call stack must be able to handle the effect? And if that's part of the call stack, why not just make that an explicit function?
- duped 3y ago> Does that mean part of the function signature needs to include that the call stack must be able to handle the effect? Some people would argue that the effects a function may raise are indeed a part of a function's type and for it to type check it must be within scope of a handler for that effect. Think of it like function coloring, a function with the "async/await" effect means all callees must be invoked either within an "async" block (another function with the await effect) or within scope of a runtime that handles the effect. But even that is a bit limiting. > And if that's part of the call stack, why not just make that an explicit function? This is kind of a meaningless question depending on how you formulate it, because the big thing about things like effects is that the callee both returns more than once and is called more than once - so the notion of a "call stack" is kind of meaningless. For example if f calls g and g raises E1 and then E2, f can declare a handler for E1 which moves it into a different scope that has a handler for E2. From the perspective of g() there is no function to call to deal with E1 and E2, since it's the responsibility of the caller to determine that.
- duped 3y agoLook at it less like a first class language feature and more like a language feature for implementing others in terms of it.
- smt88 3y ago> express programs in as few expressions as possible I don't find this appealing on its face. Extreme terseness, even when done very elegantly, makes programs very hard to read and sometimes also hard to maintain. I suspect that's one of the reasons Lisps (and functional languages in general) haven't caught up in popularity even as it becomes much easier to adopt one for any target and in any organization.
- duped 3y ago> Extreme terseness, even when done very elegantly, makes programs very hard to read and sometimes also hard to maintain. The advantage to something like call/cc is not that you can make code extremely terse but rather that there is one code path for analyzing control flow and each variant of control flow isn't a special case. It's also not more terse at all, but rather more explicit. You don't need to force users to use it, but it is useful to define more useful mechanisms like if/else if, match/switch, throw/catch/finally, coroutines, etc in terms of an abstraction that doesn't break type checking or codegeneration. All that said "call/cc considered harmful" is an old take
- Yoric 3y ago> I don't find this appealing on its face. Extreme terseness, even when done very elegantly, makes programs very hard to read and sometimes also hard to maintain. In that case, it's more about minimizing the language, rather than the programs. call/cc is a single instruction that is sufficiently expressive to implement e.g. exceptions, coroutines and lots of stuff for which people typically use monad-style embeddings. Making the language easier to specify is generally a good thing, because it makes all forms of static analysis and compilation easier (at least theoretically) and because there are fewer places to hide bugs in the compiler/interpreter. Of course, you move the potential bugs to the libraries, but that's generally considered better, because it's easier to debug. > I suspect that's one of the reasons Lisps (and functional languages in general) haven't caught up in popularity even as it becomes much easier to adopt one for any target and in any organization. Well, to be fair, functional constructs have made it to pretty much all mainstream languages these days.
- 3y ago
- nerdponx 3y agoGeneralized exception-throwing is pretty much the Common Lisp condition system.
- amno 3y agoThat was pretty much my first thought too when I was reading the article.
- sparkie 3y agoThe thing this appears to be missing is the ability to capture the part of the stack between the try and hurl as a first-class value and reify it later, although the toss/return is not too dissimilar as toss does capture the stack and return reifies it. It's close to multi-prompt due to multiple catch branches, so I would describe it as a one-shot, multi-prompt, second-class delimited continuation. A bit more powerful are multi-shot, multi-prompt, first-class delimited continuations, which you can then use to implement exceptions themselves with multiple catch blocks.