Also from the tutorial:
"Unlike Eff, Koka, Links, and other languages that support effect handlers, effects in Multicore OCaml are unchecked currently. A program that does not handle a performed effect fails with a runtime error."
For anyone using a dark theme with their browser or OS, the string constants actually have interpolation syntax in them. It's just black, on a black background. For example, I thought it was strange their error message was:
"Error: "
Turns out its actually:
"Error: ${msg}"
voat · 4h ago
The algebraic effect handler pattern is a much simpler mental model than monads. And is transferrable to other languages pretty easily. See something like https://github.com/doeixd/effectively in Typescript
mrkeen · 4h ago
I use effects to make guarantees about what the code will and won't do.
When Option was retrofitted onto Java, the NPEs didn't disappear. We got Options that could be null, but more importantly, devs (and the standard library) continued to use null liberally. We never ended up at the tautological goal of "if I have a Person, then I have a Person".
I am equally skeptical of bolting on effect systems after the fact. Even if one of these effect systems becomes 'the main one' and starts showing up in standard libraries, we'll still end up with declared-effectful code and undeclared-effectful code, but will again fall short of the goal, declared-effectful code.
sidkshatriya · 41m ago
The impact of Effects are a bit less pervasive than null wouldn’t you say ? Effects can be used with code “in between” that knows nothing about effects. Performing and handling effects can be quite transparently
IshKebab · 2h ago
I agree, this seems like something where it really needs to be natively supported in the language from day one to work properly.
epolanski · 4h ago
1. The effect data type admits an instance of monad, like arrays or option or result. Not sure why would you put those in competition, an effect is a supercharged ReaderResult. The only differences are in naming choices, traverse being called foreach, flat map being called "and then", etc.
2. There is a much more popular and robust implementation of effects for Typescript: https://effect.website/
msk-lywenn · 1h ago
Are algebraic effects a recreation of common lisp’s condition system?
alethic · 31m ago
They are similar, but effect handlers are more powerful and more amenable to typing.
I _think_ the distinction is that the interface tells you what MovieAPI _does_, but this system also tracks what are contextual requirements to do that thing.
So when they write their version of the MovieAPI that calls out to an existing web api, they have to write `\ {Net, IO}` on the main method that uses them.
When they write their test that uses a canned recommendation, they can just write `\ {IO}`, and they have a complicated pile of test logic and confirm just at a glance at the top-level signature that no network calls are made when the test runs.
So in java, you could write two distinct classes implementing the MovieApi interface as you defined it, one of which calls out to the web and one which doesn't, and nothing about the type indicates that difference. If you accidentally used the wrong class, your tests could make a network call and you might never notice, because you would not have tracked that effect.
For someone with a java background, it's really helpful to make the analogy to Checked Exceptions, which let us propagate information about what kind of handler must be required contextually.
alethic · 1h ago
The checked exceptions analogy is a good one. Thinking of effect handlers as resumable checked exceptions with some syntactic sugar is very accurate. For someone with a Haskell background, thinking about them as "dependency injection" is also helpful (and these notions are equivalent!) but for the Java heads out there, yeah, resumable checked exceptions provides a really good mental model for what effect handlers are doing with the call stack in the general case.
iroddis · 2h ago
I wasn’t aware of effects until I read the article, but I like the idea. The example you have is just an interface. Effects work a bit like boundary hooks. A pure function only works on things internal to its scope, and so it has no effects. An impure function might affect things outside of its scope, and may want to allow the caller to hook when those effects occur. Think a function that uses the gpu. That’s a side effect, and the function should be decorated to reflect that.
I’m guessing some languages would allow for checking to ensure that any induced effects are correctly noted and advertised so that the system as a whole can be more easily reasoned about.
alethic · 1h ago
It's similar on the surface. Another language, Effekt, does actually use interfaces for their effect declarations rather than having a separate `eff` declaration.
The difference comes in their use. There's two things of note.
First, the implementation of an interface is static. It's known at compile time. For any given concrete type, there is at most one implementation of MovieApi. You're using the interface, then, to be generic over some number of concrete types, by way of only specifying what you need. Effect handlers aren't like this. Effect handlers can have many implementations, actually. This is useful in the case of ex. adding logging, or writing tests to simulate I/O without actually doing it, or just having different behavior at different places across the program / call stack...
eff MovieApi {
def getPopularMovies();
}
def main() {
run {
println("Alice's movies: ", getPopularMovies());
} with handler MovieApi {
def getPopularMovies() = [
"Dr. Strangelove",
"Lawrence of Arabia",
"The End of Evangelion",
"I Saw the TV Glow"
];
}
run {
println("Bob's movies: ", getPopularMovies());
} with handler MovieApi {
def getPopularMovies() = [
"The Magic School Bus: Space Adventures",
"Spy Kids 3-D: Game Over",
"Twilight: Breaking Dawn: Part II"
];
}
}
Second, the effects of effect handlers are not functions. They're under no obligation to "return", and in fact, in many of the interesting cases they don't. The `resume` construct mentioned in the article is a very special construct: it is taking the "continuation" of the program at the place where an effect was performed and providing it to the handler for use. The invocation of resume(5) with a value looks much like a return(5), yes. But: a call to resume 1) doesn't have to happen and the program can instead continue after the handler i.e. in the case of an effectful exception, 2) doesn't have to be invoked and the call to resume can instead be packaged up and thunkified and saved to happen later, and 3) doesn't have to happen just once and can be invoked multiple times to implement fancy backtracking stuff. Though this last one is a gimmick and comes at the cost of performance (can't do the fast call stack memcpy you could do otherwise).
So to answer your question more briefly, effects differ from interfaces by providing 1) a decoupling of implementation from use and 2) the ability to encompass non-local control flow. This makes them not really compete with interfaces/classes even though the syntax may look similar. You'd want them both, and most effectful languages have them both.
mrkeen · 4h ago
> Solving the “what color is your function” problem.
Eh. I currently use monads for my effects, and am open to the idea of language-based effects.
But this isn't a solution to the colour problem - it is the colour problem. The compiler stops you accidentally calling red from blue. If you want to do it deliberately, you change the caller from blue to red.
epolanski · 4h ago
This depends on the implementation. In effect typescript land, synchronous and asynchronous code is treated equally as soon as it's lifted in an effect.
artemonster · 5h ago
"The only one that I know of besides Flix is Unison." huh? Koka, Effekt
saviorand · 1h ago
Koka, Effekt and 4 other research languages are mentioned earlier in the article though? This talks about practical languages, not research-only
Other pages also contain some more advanced details and casestudies on effect handling
artemonster · 5h ago
I really want effects to shine and thrive, but since this is a very academic topic, only academic people engage with the research and it comes with academic pedantism, perfectionist worldview, strange attraction to beautiful consistency, etc. This automatically places effect research FAR from any practicality and grounded realism. 2 points, as examples:
1. Ever tried adding a simple print statement for debugging purposes while coding in effectful lang? compiler: "NNNOOOOO!!!! THIS IS AN ERROR; I WILL NEVER COMPILE THIS NONSENSE YOU __MUST__ SPECIFY CONSOLE EFFECT WAAARGHH!11"
2. multi-resumable stacks. how many times you really want to implement custom backtracking for multi resumable computations? this is such an obscure nonsensical use case that is hard to nearly impossible to solve efficiently, but everyone wants to support it. supporting this adds enormous complexity and kills any potential for performance. WHYYYYYYYYY. and yet they focus on this as a holy grail feature but whever there is a showcase they use synthetic "choice" example, lol.
To be fair, presumably debug printig could be "escaped" from the effect type checking if the designer of an effect system would want it. For instance, debug printig in Haskell completely sidesteps the need for the IO Monad and just prints in whatever context
artemonster · 5h ago
yeah, most times its solved by side-stepping the strict type system and making an exception for debug prints. but this is not a real practical solution, this is a stupid workaround born from overinsistence on "beautiful" design choices.
thfuran · 4h ago
It seems to me like a pragmatic compromise and very much a real solution. What would you consider a real solution that isn’t overinsisting on beautiful design choices?
artemonster · 4h ago
putting strong static type system into optional compiler pass. yes, I know this may be null is some cases, let me run my program for now, I know what I am doing. yes, there are unhandled effects or wrong signature, just let me run my test. yes, that type is too generic, i will fix it later, let me run my god damn program.
noelwelsh · 4h ago
This puts a lot of extra conditions on the runtime; you basically have to implement a "dynamically typed" runtime like Javascript. In doing so you lose a lot of performance. Google have invested something like a century of man-hours into V8 and on typical benchmarks the performance is about half of Java's, which in turn is typically about half of C / Rust's performance. That's a pretty big compromise for some.
Sharlin · 4h ago
I’m sure default effects could handle this, just as Rust has some auto traits and default trait bounds. It doesn’t even have to be the full console i/o effect, it can be a specific debug effect that outputs to console in a dev build and to a file or devnull or whatever in release builds, or you could disable it in release builds and the compiler ensures there aren’t stray debug calls left, without needing a specific lint for that.
(Rust does have a similar problem in that debug printing requires the Debug trait bound which can be really annoying when writing generic code. Would be nice if there were a sprinkle of RTTI involved.)
epolanski · 4h ago
1. Nonsense [1]
2. It's implementation dependent, but of course you lose tracing, etc if you want to just log with language primitives, which is why you shouldn't when every effect system offers you tools to do so. If you want a system where each side effect is traced, monitored and encoded as a recipe, then you use the effectful version.
Also from the tutorial: "Unlike Eff, Koka, Links, and other languages that support effect handlers, effects in Multicore OCaml are unchecked currently. A program that does not handle a performed effect fails with a runtime error."
When Option was retrofitted onto Java, the NPEs didn't disappear. We got Options that could be null, but more importantly, devs (and the standard library) continued to use null liberally. We never ended up at the tautological goal of "if I have a Person, then I have a Person".
I am equally skeptical of bolting on effect systems after the fact. Even if one of these effect systems becomes 'the main one' and starts showing up in standard libraries, we'll still end up with declared-effectful code and undeclared-effectful code, but will again fall short of the goal, declared-effectful code.
2. There is a much more popular and robust implementation of effects for Typescript: https://effect.website/
https://lobste.rs/s/q8lz7a/what_s_condition_system_why_do_yo...
So in java, you could write two distinct classes implementing the MovieApi interface as you defined it, one of which calls out to the web and one which doesn't, and nothing about the type indicates that difference. If you accidentally used the wrong class, your tests could make a network call and you might never notice, because you would not have tracked that effect.
For someone with a java background, it's really helpful to make the analogy to Checked Exceptions, which let us propagate information about what kind of handler must be required contextually.
I’m guessing some languages would allow for checking to ensure that any induced effects are correctly noted and advertised so that the system as a whole can be more easily reasoned about.
The difference comes in their use. There's two things of note. First, the implementation of an interface is static. It's known at compile time. For any given concrete type, there is at most one implementation of MovieApi. You're using the interface, then, to be generic over some number of concrete types, by way of only specifying what you need. Effect handlers aren't like this. Effect handlers can have many implementations, actually. This is useful in the case of ex. adding logging, or writing tests to simulate I/O without actually doing it, or just having different behavior at different places across the program / call stack...
Second, the effects of effect handlers are not functions. They're under no obligation to "return", and in fact, in many of the interesting cases they don't. The `resume` construct mentioned in the article is a very special construct: it is taking the "continuation" of the program at the place where an effect was performed and providing it to the handler for use. The invocation of resume(5) with a value looks much like a return(5), yes. But: a call to resume 1) doesn't have to happen and the program can instead continue after the handler i.e. in the case of an effectful exception, 2) doesn't have to be invoked and the call to resume can instead be packaged up and thunkified and saved to happen later, and 3) doesn't have to happen just once and can be invoked multiple times to implement fancy backtracking stuff. Though this last one is a gimmick and comes at the cost of performance (can't do the fast call stack memcpy you could do otherwise).So to answer your question more briefly, effects differ from interfaces by providing 1) a decoupling of implementation from use and 2) the ability to encompass non-local control flow. This makes them not really compete with interfaces/classes even though the syntax may look similar. You'd want them both, and most effectful languages have them both.
Eh. I currently use monads for my effects, and am open to the idea of language-based effects.
But this isn't a solution to the colour problem - it is the colour problem. The compiler stops you accidentally calling red from blue. If you want to do it deliberately, you change the caller from blue to red.
Other pages also contain some more advanced details and casestudies on effect handling
(Rust does have a similar problem in that debug printing requires the Debug trait bound which can be really annoying when writing generic code. Would be nice if there were a sprinkle of RTTI involved.)
2. It's implementation dependent, but of course you lose tracing, etc if you want to just log with language primitives, which is why you shouldn't when every effect system offers you tools to do so. If you want a system where each side effect is traced, monitored and encoded as a recipe, then you use the effectful version.
[1] https://effect.website/play#769a55e0ea0a