For those who've used effectful languages, how much of a spaghetti code mess do they create, potentially? That's basically my only worry with effects that can be suspended and resumed at basically any point and any location in the codebase.
Effects can pile up for sure, but with Koka's effect polymorphism it is actually quite a bit less than I first expected. (I've been contributing to Koka recently, and writing a lot of Koka code). Type aliases for effects that are commonly used together also help with typing. With the strong typing discipline and good type inference it is actually quite easy to make your code a lot cleaner than other languages in my opinion. You don't have as much boilerplate such as those introduced by monad transformers in Haskell, and since everything is purely functional it is a lot easier to reason about than other languages.
Preemptive multitasking is an effect handler implemented by the operating system. There are very few instances where you have to worry about process switching by the OS, basically only when you have a handle to a resource outside of your process.
A similar constraint will apply to algebraic effects, which is why they're algebraic: only if your code directly depends on some effect will you have to care about effects.
I don't quite understand, let's say I have a bunch of effects in my code, all depending on one another. Will that make a mess? If so, how does one avoid that, or is that an inherent issue in algebraic effects systems?
> I don't quite understand, let's say I have a bunch of effects in my code, all depending on one another. Will that make a mess?
It depends entirely on what those effects and dependencies actually are. If you really want to, you can use an effect system as a dynamically scoped imperative language, just like you can write all your Haskell code in `IO` or use exceptions for control-flow in C++. The value proposition is that you have to do it on purpose.
I've used Rx style libraries in static languages but I've found that it created a mess over time, even with their intended idiomatic usage, so I was just wondering if it were the same for effects, even if we use them in static languages how effects would be intended to be used there.
> even if we use them in static languages how effects would be intended to be used there.
There's not much that can be said about how effects, in general, are intended to be used. Algebraic effects subsume basically every control flow construct in common use: exceptions, transactional semantics, cooperative multitasking, search, probabilistic programming, IO... non-delimited continuations are the only exception I can think of. Unless you only ever write provably-total pure functions, you're always using effects of some sort. And even then, you might want to think of your program as effectful even when you don't have to: reading `a -> Maybe b` as "`a -> b` with the possibility of failure" is a classic example.
Thanks, I think I'll have to actually do some coding with effects (as I haven't really used them) to understand exactly how they work and their pros and cons. Any thoughts on which language to start off with, is Koka good as a beginner effects language, or is another one like OCaml 5 recommended?
I wouldn't worry about language-level support at the moment: there aren't as far as I know any mature implementations yet, and even if there were libraries are usually better for learning purposes.
Personally I would recommend Haskell. Native row types are coming "eventually" and always will be, so there's some ceremony involved, but it's the ecosystem with by far the best alternative solutions to the problems algebraic effects are supposed to deal with: idiomatic javascript can make any programming discipline look good, whereas it'll be much more illuminating to compare algebraic effects to a more traditional `mtl`-style approach.
Your question just isn't answerable. It's like asking if lambdas create a mess over time because you can now write in continuation passing style, or if having mutation in a language creates a mess over time because you can create globally mutable variables. How would you answer those questions?
Well, some are messier than others, of course. If I use gotos all over my code, it'll be messier than writing everything with functions, much less pure functions.
You mean a situation where the effect handler for effect A invokes effect B? I wonder if the compiler is able to detect and prevent "circular" event invocations.