10 ms·
Why Algebraic Effects?
- charcircuit 1y agoThis doesn't give a focused explaination on why. I don't see how dependency injection is a benefit when languages without algebraic effects also have dependency injection. It doesn't explain if this dependency injections is faster to execute or compile or what.
- yen223 1y agoThe way dependency injection is implemented in mainstream languages usually involves using metaprogramming to work around the language, not with the language. It's not uncommon to get errors in dependency-injected code that would be impossible to get with normal code. It's interesting to see how things can work if the language itself was designed to support dependency injection from the get-go. Algebraic effects is one of the ways to achieve that.
- vlovich123 1y agoDon’t algebraic effects offer a compelling answer to the color problem and all sorts of related similar things?
- threeseed 1y agoBut they also introduce their own color-like problems. For example with Scala we have ZIO which is an effect system where you wrap all your code in their type e.g. getName(): ZIO[String]. And it doesn't matter if getName returns immediately or in the future which is nice. But then the problem is that you can't use normal operators e.g. for/while/if-else you need to use their versions e.g. ZIO.if / ZIO.repeat. So you don't have the colour problem because everything is their colour.
- OtomotO 1y agoBut that's only a problem if it's a library and not done on the language level?!
- threeseed 1y agoBut in the research languages listed they still are colouring function types. So it doesn't seem to matter whether it's a library or in the language. Either everything is an effect. Or you have to deal with two worlds of code: effects and non-effects.
- vlovich123 1y agoMaybe but even if that's the case, you've now got just plain and colored vs before you'd have plain, color A, color B, color C if you go beyond just async as the only other color (e.g. whether you're accepting something by reference or value, whether it's a mutable reference, whether or not the function is compile-time, etc etc etc). Still seems better to me if you collapse colors >= 1 into a single language system.
- sullyj3 1y agoFunctions can be polymorphic in their effectfulness, so the coloring problem isn't. Functions only become incompatible where you've made them incompatible on purpose - the whole point of annotating functions' effectfulness is to statically know you're not accidentally invoking particular effects where you promised you wouldn't.
- jfecher 1y agoDeveloper of Ante here. Functions in languages with effect systems are usually effect polymorphic. You can see the example in the article of a polymorphic map function which accepts functions performing any effect(s) including no effect(s). For this reason effect systems are one of the solutions to the "what color is your function" problem.
- charcircuit 1y ago>It's interesting Which is why I was asking for that interesting thing to be written in the article on why it would better.
- cryptonector 1y agoIt's the same as with monads: 1) Testing. Write pure code with "effects" but, while in production the effects are real interactions with the real world, in testing they are mocked. This allows you to write pure code that does I/O, as opposed to writing pure code that doesn't do I/O and needs a procedural shell around it that does do the I/O -- you get to write tests for more of your code this way. 2) Sandboxing. Like in (1), but where your mock isn't a mock but a firewall that limits what the code can do. (2) is a highly-desirable use-case. Think of it as a mitigation for supply-chain vulnerabilities. Think of log4j. Both of these are doable with monads as it is. Effects can be more ergonomic. But they're also more dynamic, which complicates the implementations. Dynamic features are always more costly than static features.
- charcircuit 1y agoAgain you are listing things that are possible but not explaining why it's better to do it via algebraic effects as opposed to the alternatives. For example if you were in a meeting with Oracle to try and convince them to invest 100 million dollars for adding algebraic effects to Java and its ecosystem how would you convince them it would be providing enough value to developers to justify it over some other improvement they may want to do. For example, "Writing mocks for tests using algebraic effects is better than using jmock because ..."
- cryptonector 1y agoThe only reason I can think of -but I'm not the right person to ask- is ergonomics, that in many cases it might be easier to push an effect handler than to build a whole monad or whatever. Elsewhere in this thread there's talk of effects solving the color problem.
- threeseed 1y agoTake the following requirement: "A user has made an API call. I want you to in parallel race two concurrent tasks: check if the data is in (1) cache and (2) database. Whichever returns fastest return to the user. Otherwise kill the other task mid-flight and make sure the connection resources for both are cleaned up". This is trivial with an effect systems like ZIO and it will work flawlessly. That's the benefit of effect systems. Use cases like this are made easy. But now with JVM Virtual Threads there are frameworks like Ox: https://github.com/softwaremill/ox https://github.com/softwaremill/ox which allow you to achieve the same thing without effects. And how many times do you really need that sort of capability ?
- iamwil 1y agoBecause Algebraic Effects are unfamiliar, we explain a part of it with something that's familiar to most developers. This is just like the story of the blind men with the elephant. The elephant (algebraic effects) is a whole other different beast, but to help the beginner understand the first parts of it, we say the truck is like a snake (resumable exceptions) or that the ear is like a fan (dependency injection). Algebraic effects are a new type of control flow. When an effect is raised, the handler (defined further up the call stack) has several options: - handle the effect and resume the computation where the effect was raised. - handle the effect, but resume the computation multiple times back to where the effect was raised. - decide not to handle the effect, and just exit, at which point the execution resumes right when the handler was called further up the stack, and everything executed since then is as if it never happened. This is a way of doing back tracking. In addition, what's algebraic about algebraic effects is that they usually come in a group, and are designed with a compositional algebra, so you can safely compose them in different ways. A common one is commutativity, so it doesn't matter what order you execute them in. Of course, not all algebraic effects have commutativity, as that has to be designed by the implementer. Monads are different in that they often don't compose without monad transformers, so that can be finicky. Hence, with all these things together, you can use them to implement control flow in other languages that are typically baked into the language, such as exceptions, coroutines, generators, async/await, probabilistic programming, backtracking search, dependency injection. You might also invent your own! But most commonly here, we use them to separate the *intent* to do a side-effect from the actual execution of the side-effect. That way, side-effects are controlled, and it becomes easier to reason about our code.
- cdaringe 1y agoI did protohackers in ocaml 5 alpha a couple of years ago with effects. It was fun, but the toolchain was a lil clunky back then. This looks and feels very similar. Looking forward to seeing it progressing.
- abathologist 1y agoEffects in OCaml 5.3 are quite a bit cleaner than there were a few years back (tho still not typed).
- AdieuToLogic 1y ago> You can think of algebraic effects essentially as exceptions that you can resume. How is this substantively different than using an ApplicativeError or MonadError[0] type class? > You can “throw” an effect by calling the function, and the function you’re in must declare it can use that effect similar to checked exceptions ... This would be the declared error type in one of the above type classes along with its `raiseError` method. > And you can “catch” effects with a handle expression (think of these as try/catch expressions) That is literally what these type classes provide, with a "handle expression" using `handleError` or `handleErrorWith` (depending on need). > Algebraic effects1 (a.k.a. effect handlers) are a very useful up-and-coming feature that I personally think will see a huge surge in popularity in the programming languages of tomorrow. Not only will "algebraic effects" have popularity "in the programming languages of tomorrow", they actually enjoy popularity in programming languages today. https://typelevel.org/cats/typeclasses/applicativemonaderror.html https://typelevel.org/cats/typeclasses/applicativemonaderror...
- anon-3988 1y agoI don't really get it, but is this related to delimited continuation as well?
- tempodox 1y agoYes, see for instance https://github.com/ocaml-multicore/ocaml-effects-tutorial https://github.com/ocaml-multicore/ocaml-effects-tutorial.
- cryptonector 1y agoThat's just an implementation detail. I don't think there's anything about effects that _requires_ delimited continuations to implement them.
- tome 1y agoIf you want multishot continuations then I don't really know of any way other than delimited continuations (other than undelimeted continuations or simulating delimited continuations, on the heap).
- wild_egg 1y ago> You can think of algebraic effects essentially as exceptions that you can resume. So conditions in Common Lisp? I do love the endless cycle of renaming old ideas
- riffraff 1y agoAlso literal "resumable exceptions" in Smalltalk.
- valcron1000 1y agoNo, algebraic effects are a generalization that support more cases than LISP's condition system since continuations are multi-shot. The closest thing is `call/cc` from Scheme. Sometimes making these parallelism hurts more than not having them in the first place
- wild_egg 1y agoAh multi-shot does make a big difference, thanks for clarifying!
- ww520 1y agoAlso dependency injection.
- Rusky 1y agoWhat a thought-terminating way to approach an idea. Effects are not simply renamed conditions, and we have a whole article here describing them in more detail than that one sentence, so you can see some of the differences for yourself.
- nemo1618 1y agoI see two downsides. Looking at this snippet: my_function (): Unit can AllErrors = x = LibraryA.foo () y = LibraryB.bar () The first thing to note is that there is no indication that foo or bar can fail. You have to lookup their type signature (or at least hover over them in your IDE) to discover that these calls might invoke an error handler. The second thing to note is that, once you ascertain that foo and bar can fail, how do you find the code that will run when they do fail? You would have to traverse the callstack upwards until you find a 'with' expression, then descend into the handler. And this cannot be done statically (i.e. your IDE can't jump to the definition), because my_function might be called from any number of places, each with a different handler. I do think this is a really neat concept, but I have major reservations about the readability/debuggability of the resulting code.
- MrJohz 1y ago> And this cannot be done statically (i.e. your IDE can't jump to the definition), because my_function might be called from any number of places, each with a different handler. I believe this can be done statically (that's one of the key points of algebraic effects). It works work essentially the same as "jump to caller", where your ide would give you a selection of options, and you can find which caller/handler is the one you're interested in.
- HelloNurse 1y agoThis suggests a novel plausible IDE feature: list callers of a function, but only those with a handler for a certain effect (maybe when the context sensitive command is invoked on the name of one of the effects rather than on the name of the function).
- codethief 1y agoWhat do you mean by "only those"? Each caller of an effectful function will have to handle the effect somehow, possibly by propagating it further up the chain. Is that what you meant, an IDE feature to jump to the ((grand-)grand-)parent callers defining the effect handler?
- knuckleheads 1y agoFirst time in a long while where I’ve read the intro to a piece about new programming languages and not recognized any of the examples given at all even vaguely. How times change!
- rixed 1y agoMaybe I'm too archaic but I do not share the author's hope that algebraic effects will ever become prevalently used. They certainly can be useful now and then, but the similitude with dynamic scoping brings too many painful memories.
- threeseed 1y agoI wouldn't worry. Even the simplest and most friendly effects library: https://effect.website https://effect.website Shows clearly why they will never be a mainstream concept. The value proposition is only there when you have more elaborate concurrency needs. But that is a tiny fraction of the applications most people are writing today.
- lblume 1y ago> the simplest and most friendly effects library Highly unlikely. JavaScript simply isn't a language built with this kind of evaluation model in mind, so an external library introducing completely orthogonal concepts surely should not be modeled as "most friendly". Don't get me wrong — Effect can be great! But the library also deliberately does not market itself as simple, rather as modular and multi-faceted, a "toolbox" from which developers may (should?) choose some tools and omit others. A language that supports effects first-hand, like the proposed "Ante", would provide a much a more expressive, possibly simpler, and definitely friendlier approach than TS effect ever could.
- marcosdumay 1y agoYeah, if you impose that effects must only be used to enforce code correctness, then only code that is hard to write correctly will benefit.
- deleted 1y ago[deleted]
- codethief 1y ago> The value proposition is only there when you have more elaborate concurrency needs I see the value every day when I look at a shitty piece of JavaScript or Python code that secretly modifies some global state behind the scenes. Or when I want to do dependency injection without resorting to frameworks doing some magic. Yes, I could drill my dependencies through the entire call stack, and often do because it's nicely explicit. But stuff like `logger` really doesn't belong in the function signature if it can be avoided.
- ollysb 1y agoAs I understand it this was the inspiration for React's hooks model. The compiler won't give you the same assurances but in practice hooks do at least allow to inject effects into components.
- YuukiRey 1y agoI don’t see the similarity. Since hooks aren’t actually passed to, or injected into components, there’s no way to evaluate the same hooks in different ways. I can’t have a hook that talks to a real API in one environment but to a fake one in another. I’d have to use Jest style mocking, which is more like monkey patching. From the point of view of a React end user, there’s also no list of effects that I can access. I can’t see which effects or hooks a component carries around, which ones weren’t yet evaluated, and so on.
- ollysb 1y agoYou're right, it's the use of the Context that allows for the injection of the effects. It's also all handled at runtime which does unfortunately mean that the contexts supplying the effects can't be required at compile time.
- tubthumper8 1y agoThis post from the React team has more detail on that topic: https://overreacted.io/algebraic-effects-for-the-rest-of-us/ https://overreacted.io/algebraic-effects-for-the-rest-of-us/
- ww520 1y agoIt's different. Hooks in React is basically callback on dependency of state changes. It's more similar to the signaling system.
- deleted 1y ago[deleted]
- nabla9 1y agoYet another thing Common Lisp programmers have been doing since the time of hoe and axe.
- ctenb 1y agoBut Lisp is untyped, which is a major difference. EDIT: dynamically typed I mean
- deleted 1y ago[deleted]
- nikita2206 1y agoHave you thought of using generators as a closest example to compare effects to? I think they are much closer to effects than exceptions are. Great explainer anyway, it was tge first time I have read about this idea and it was immediately obvious
- yen223 1y agoExceptions are usually used because the syntax for "performing" exceptions (throw) vs handling exceptions (try-catch) is familiar to most programmers, and is basically the same as the syntax for "performing" an effect vs handling the effect, except that the latter also includes resuming a function. It would be cool to see how generators will be implemented with algebraic effects.
- iamwil 1y agoFull blown algebraic effects are multi-shot, meaning you can resume from the same raised effect multiple times. Generators can only resume from a point in the execution a single time. But yes, you can implement single-shot effects with generators.
- yyyk 1y agoThe state effects example seems unlike the others - the examples avoid syntax for indentation, omit polymorphic effect mention and use minimal syntax for functions - but for state effects you need to repeat "can Use Strings" each function? Presumably one may want to group those under type Strings or can Use Strings, at which point you have a namespace of sorts...
- thdhhghgbhy 1y agoJust seems to be a way of organising code really.
- aatd86 1y agothat's a good question actually. We can define and assign side effects. But is it sufficient to control the list of side effectful operations of a function? Can the compiler check that a function is pure besides its assigned side effects? What about state modified via closures? Or is it only for system side-effects like filesystem operations?
- thdhhghgbhy 1y ago>Can the compiler check that a function is pure besides its assigned side effects? The compiler can't. It is up to the programmer to make sure that a database effect doesn't, say for sake of example, write out to a file also. So in the end this is just a novel new way to organise code.
- jfecher 1y agoIf the database effect wrote to a file it'd require the `IO` effect and code using it would need that effect as well. A compiler can generally show a function to be free of most side effects if it uses no effects. The exceptions to this are things like divergence. As long as the language is Turing complete you can't prove it won't loop forever of course. Another exception could be extern functions which the compiler can't verify the correctness of the type signature. Different languages handle these differently but if users are allowed to write any (and the language doesn't force them to have an IO effect) then they can be a source of unsafety. Languages like Koka and Effekt are considered pure though and enforce this through their effect systems.
- aatd86 1y agoso there is side effects propagation and inference I guess. That has to require some help from the tooling/IDE.
- practal 1y agoAlgebraic effects seem very interesting. I have heard about this idea before, but assumed that it somehow belonged into the territory of static type systems. I am not a fan of static type systems, so I didn't look further into the idea. But I found these two articles [1] about an earlier dynamic version of Eff (the new version is statically typed), which explains the idea nicely without introducing types or categories (well, they use "free algebra" and "unique homomorphism", just think "terms" and "evaluation" instead). I find it particularly intriguing that what Andrej Bauer describes there as "parameterised operation with generalised arity", I would just call an abstraction of shape [0, 1] (see [2]). So this might be helpful for using concepts from algebraic effects to turn abstraction algebra into a programming language. [1] https://math.andrej.com/2010/09/27/programming-with-effects-i-theory/ https://math.andrej.com/2010/09/27/programming-with-effects-... [2] http://abstractionlogic.com http://abstractionlogic.com
- nicoty 1y agoWhat's wrong with static type systems?
- hinoki 1y agoForget it Jake, it’s land of Lisp.
- practal 1y agoI've summarized my opinion on this here: https://doi.org/10.5281/zenodo.15118670 https://doi.org/10.5281/zenodo.15118670 In normal programming languages, I see static type systems as a necessary evil: TypeScript is better than JavaScript, as long as you don't confuse types with terms... But in a logic, types are superfluous: You already have a notion of truth, and types just overcomplicate things. That doesn't mean that you cannot have mathematical objects x and A → B, such that x ∈ A → B, of course. Here you can indeed use terms instead of types. So in a logic, I think types represent a form of premature optimisation of the language that invariants are expressed in.
- frumplestlatz 1y ago
- xixixao 1y agoIt feels powerful. I think the effects in return types could be inferred. But I share the concerns of others about the downsides of dependency injection. And this is DI on steroids. For testing, I much prefer to “override” (mock) the single concrete implementation in the test environment, rather than to lose the static caller -> callee relationship in non-test code.
- nevertoolate 1y agoWhen I see a new (for me) idea coming from (presumably) category theory I wonder if it really will land in any mainstream language. In my experience having cohesion on the philosophical level of the language is the reason why it is nice to work with it in a team of programmers who are adept in both programming and in the business context. A set of programming patterns to solve a problem usually can be replaced with a possibly disjunct set of patterns where both solutions have all the same ilities in the code and solve the business problem. My question is - can a mainstream language adopt the algebraic effects (handlers?) without creating deep confusion or a new language should be built from the ground up building on top of these abstractions in some form.
- nwienert 1y agoReact hooks are them, basically. Not at the language level, but widely adopted and understood.
- ww520 1y ago> can a mainstream language adopt the algebraic effects (handlers?) without creating deep confusion or a new language should be built from the ground up building on top of these abstractions in some form. Algebraic Effect is a variant/enhancement of dependency injection formalized into a language. Dependency injection has massive usage in the wild for a long time with just library implementation.
- threeseed 1y ago> Algebraic Effect is a variant/enhancement of dependency injection Every library so far that has implemented effects e.g. Cats, ZIO, Effects has done so to make concurrency easier and safer. Not for dependency injection.
- iamwil 1y agoA weak form of algebraic effects are already very common: React hooks. React hooks are different from full-blown algebraic effects in a couple ways: - The handler for the hooks are already implemented for you, and you can't swap it out for a different handler. For example, the implementation of useState is fixed, and you can't swap it out for a different implementation. - They're not multishot resumption. When a hook is raised, the handler can only handle it and resume it once. In a full-blown algebraic effect, the handler can resume the same raised effect multiple times. - Algebraic effects usually come bundled together. Those effects have specific compositional rules with each other. That's how they're algebraic.
- thrance 1y agoI like the idea of Algebraic effects but I'm a little skeptical of the amount of extra syntax. Let's say I'm building a web server, my endpoint handler now needs to declare that it can call the database, call the s3, throw x, y and z... And same story for most of the functions it calls itself. You solved the "coloration problem" at the cost of adding a thousand colors. Checked exceptions are the ideal error handling (imho) but no one uses them properly because it's a hassle declaring every error types a function may return. And adding an exception to a function means you need to add/handle it in many of its callers, and their callers in turn, etc.
- hocuspocus 1y agoTwo things: - Only your outermost application entry-point will need to stack all effects; you'll wire the server, client, DB and so on in your `main` method that returns Unit \ (IO & Abort & Result & Transaction & Env ...). In specific modules individual functions should use only the narrowest effect. - Good ergonomics for type polymorphism and type inference are obviously paramount to adding algebraic effects in a way that is minimally invasive. It's obviously not trivial and mostly a PL research field at this point, but existing implementations show that there's potential for better DX compared to alternatives (monadic effect systems, mostly).
- michalsustr 1y agoAE (algebraic effect) are very interesting! Great article, thank you. Reading through, I have some concerns about usability in larger projects, mainly because of "jumping around". > Algebraic effects can also make designing cleaner APIs easier. This is debatable. It adds a layer of indirection (which I concede is present in many real non-AE codebases). My main concern is: When I put a breakpoint in code, how do I figure out where the object I work with was created? With explicit passing, I can go up and down the stack trace, and can find it. But with AE composition, it can be hard to find the instantiation source -- you have to jump around, leading to yo-yo problem [1]. I don't have personal experience with AE, but with python generators, which the article says they are the same (resp. AE can be used to implement generators). Working through large complex generator expressions was very tedious and error-prone in my experience. > And we can use this to help clean up code that uses one or more context objects. The functions involved still need to write `can Use Strings` in their signature. From practical point of view, I fail to see the difference between explicitly passing strings and adding the `can Use Strings` signature -- when you want add passing extra context to existing functions, you still need to go to all of them and add the appropriate plumbing. --- As I understand it, AE on low level is implemented as a longjmp instruction with register handling (so you can resume). Given this, it is likely inevitable that in a code base where you have lots of AE, composing in various ways, you can get to a severe yo-yo problem, and getting really lost in what is the code doing. This is probably not so severe on a single-person project, but in larger teams where you don't have the codebase in your head, this can be huge efficiency problem. Btw. if someone understands how AE deal with memory allocations for resuming, I'd be very interested in a good link for reading, thank you! [1]: https://en.wikipedia.org/wiki/Yo-yo_problem https://en.wikipedia.org/wiki/Yo-yo_problem
- codethief 1y ago> From practical point of view, I fail to see the difference between explicitly passing strings and adding the `can Use Strings` signature -- when you want add passing extra context to existing functions, you still need to go to all of them and add the appropriate plumbing. Couldn't the plumbing be reduced significantly through type inference? I.e. the function that invokes an effect will need the signature, the callers higher up the chain won't. Also, even if you had to adapt all the signatures, at least you wouldn't have to adjust the call sites themselves to pass in the context every single time.
- riyazahuja 1y agoWhat’s the advantage here of using effects over monads? It seems to me that all the proposed benefits of effects are reproducible/reproduced already by monads. Is it simply to get stateful actions while still being pure in a dynamic type system rather than static?
- hocuspocus 1y agoBetter ergonomics, direct style syntax, no need for monad transformers. There might be second order advantages like small performance gains compared to monadic effects.
- iamwil 1y agoMy understanding is that monads can only be composed in specific orders or not at all without monad transformers. The "algebraic" part of algebraic effects is that the effects also come with implicit compositional rules that say how you can put them together. You can design a family effects with rules like commutativity and associativity, so that you can be sure of how to compose them.
- artemonster 1y agoWith AE you get for free: generators, stackful coroutines, dependency injection, "dynamic" variables (as in anti-lexical ones), resumable exceptions, advanced error handling and much more. all packaged neatly into ONE concept. I dream of TS and Effekt some day merging :)
- evelant 1y agoYou can already have all of this goodness (and then some) in typescript https://effect.website/ https://effect.website/ -- Writing TS without Effect is difficult for me now, it's like a whole new and better language.
- aatd86 1y agoI might be a bit dense but I didn't quite get it and the examples didn't help me. For instance, the first example SayMessage. Is it supposed to be an effect? Why? From the function signature it could well be a noop and we wouldn't know the difference. Or is it arbitrarily decided? Is this all about notations for side-effectful operations?
- anon-3988 1y agoif I have to guess, it's something like this function will call fopen, fwrite, etc somewhere downstream. of course I am hoping to get corrected on this.
- lblume 1y agoIt might, if the effect is handled using these functions. You could also handle the effect using another handler, doing something entirely different. It is up to the caller how to handle effects, or propagate them up.
- scalaisneat 1y agobecause it is declared as an effect - and implements a handle. Think of it more like an interface. It turns out that many common patterns - async, IO, yielding can all be expressed with a handle - and the effect can be represented in the signature. This allows the code to have which effect its ran in, at runtime - other commenters pointed out its very similar to dependency injection.
- aatd86 1y agoOk so we are basically defining a named function signature that represents a side effect shape. Then the implementation of these side effect is done elsewhere and assigned as the capability of a function, which is now deemed as impure by the compiler. You're right, it looks a bit like an interface.
- huqedato 1y agoThis Ante "pseudocode" is wonderful! It's like Haskell with Elixir's expressiveness, flavor and practicality. A Haskell for developers. Waiting for the compiler to mature. I would love to develop apps in Ante.
- evelant 1y agoThis is neat, but you don’t need a new language to leverage these concepts. https://effect.website/ https://effect.website/ The effect library brings all of this goodness to typescript (and then some) and is robust and production ready. I hate writing typescript without it these days.
- chamomeal 1y agoI’ve heard that using effect in TS almost like using another language. Like it’s “all or nothing”, either your whole program is using effect or it’s not. But obvi you can still use all of typescript’s features and ecosystem. Do you also feel like effect is “or or nothing”? Or could you enclose certain parts of a program into effect parts?
- evelant 1y agoYou can definitely only write parts of your app in effect. That’s what I do and it works great. Just be mindful about where you want that boundary between effect and non effect parts to be and it’s easy. The effect parts end up so much more maintainable and robust.
- iamwil 1y agoIt's mistaken that you don't need a new language to implement full-blown algebraic effects. Full-blown algebraic effects are multi-shot, meaning that you can "resume the thrown exception" multiple times. The only way to do that is through delimited continuations, which isn't available as a feature in most languages, and can't be emulated by other language features--unless you reimplement the call stack in userland. In addition, the handler can decide not to resume a raised effect at all, at which point it can exit. When it does, it's as if all the computation that it did when the handler wrapped the computation didn't happen at all. It's a way of doing backtracking. This is also possible due to delimited continuations. Both are things that you can't do in the effects library.
- carterschonwald 1y agoI kinda want algebraic effects but where you can locally declare that certain effects are implicit/invisible in the types.
- deleted 1y ago[deleted]
- hcarvalhoalves 1y agoWhat once was old is new again. https://lisp-docs.github.io/cl-language-reference/chap-9/j-b-condition-system-concepts https://lisp-docs.github.io/cl-language-reference/chap-9/j-b... https://jacek.zlydach.pl/blog/2019-07-24-algebraic-effects-you-can-touch-this.html https://jacek.zlydach.pl/blog/2019-07-24-algebraic-effects-y...
- z5h 1y agoAfter spending a lot if time in Prolog, I want a nice way to implement and compose nondeterministic functions and also have a compile time type check. I’m eyeing all of these languages as a result. I’ll watch Ante as well. (Don’t forget developer tools like an LSP, tree-sitter or other editor plugins).
- jfecher 1y agoAuthor of Ante here - it actually already has an (extremely basic) LSP. Tooling more or less required for new languages out of the gate these days and I'm eyeing the debugging experience too to see if I can get replayability at least in debug mode by default.
- duped 1y agoI often see the claim that AE generalizes control flow, so you can (for example) implement coroutines. But the most obvious way I would implement AE in a language runtime is with coroutines, where effects are just syntactic sugar around yield/resume. What am I missing?
- FjordWarden 1y agoYes, this is what Effect-TS is doing for JavaScript, minus the syntactic sugar, but I don't know if this is a good idea in the end. It reminds me of the Spring framework, DI is also a form of AE, but it spreads like cancer through the code. The other day I was watching this talk[1] from EffectDays on how to use effects on the frontend and the entire talk was a dude showing boilerplate that did nothing. I think that AE is a beguiling idea, it lets you express imperative code in a functional language, but in a language like JS the added work of wrapping everything in a function feel like a step down from writing the simples JS you can imagine. Just as a counter example, there is also canvasmotion[2] which also uses coroutines to express scenarios for 2D graphics and this feels like it is making something hard easier. [1] https://www.youtube.com/watch?v=G_jp87gxILE https://www.youtube.com/watch?v=G_jp87gxILE [2] https://motioncanvas.io/ https://motioncanvas.io/
- duped 1y agoI'm not sure I follow, JS doesn't have coroutines (generator functions can be used kind of like coroutines, but for example you can't resume them with an argument).
- FjordWarden 1y agoYes it does: function* greeter() { const name = yield "What's your name?" yield `Hello, ${name}!` } const gen = greeter() console.log(gen.next().value) console.log(gen.next("Alice").value)
- duped 1y agoTIL!
- ChuckMcM 1y agoAs a coding abstraction I really like this (not sure I'm completely understanding it, but it sounds handy.) I wonder if that's because I spent a couple of years doing kernel programming at Sun? Its nice to be able to write sleep(foo) and know that when your code starts running again after that it's because foo woke you up. That saves a ton of time wiring up control flow and trying to cover all the edge cases. Caveat the memory locality question, it would be fun to initialize all your functions waiting to catch a unit to work on and then writing your algorithm explicitly in unit mutations.
- codethief 1y agoGreat blog post! I only have a theoretical understanding of algebraic effects so far and have yet to use them in practice, so please excuse this likely dumb question: In functional/declarative programming languages variable declarations can be evaluated in any order, as long as they don't depend on each other. Now if both declarations involve calls to functions with side effects, what determines the order in which these effects will be executed?
- codethief 1y agos/both/two