Comment by konstmonst

7 years ago

Am I the only one, that thinks that that is not a good feature. Instead A calling B calling C and having a well defined hierarchy and encapsulating complexity you have A calling B calling C calling maybe B calling maybe C again. I mean I see real value in have unidirectional call graphs because they are some much more easier to reason about. I feel like this is another gimmick to break abstractions and increase architectural complexity. You can't just for example take C and maybe rewrite it without having to know and touch B. This increases coupling and so is a bad idea in my book.

Effects are not about calling hierarchy.

Effects are about doing a computation dependent on wider context: IO, thread scheduling, non-deterministic computations, mutable references.

Now, in your example `A calling B calling C and having a well defined hierarchy and encapsulating complexity` you already have effects: threads are being scheduled by OS/runtime, IO is performed, memory cells are written.

The only difference your avg. imperative language with A calling B have is that Effects are implicitly baked into the language, while algebraic effects let you define your own effects as well.

So instead of `async val` you could simply do `(perform Async val)`, which would return val in the context of fiber scheduler (aka will do the necessary scheduling for continuing the computation). With effects you could extend your language with new effectful semantics without descending into metaprogramming hell/fixing the language.

In fact, you could think of Effects as of Monads, but composable.

They are a way of parameterizing what would otherwise ambient authority.

Rather than needing to pass your file system accessor object, logging object, network interface, database connection provider, etc all the way through your call graph A -> B -> C, you can access those using apparently ambient authority, but still live the code testable, and all without IoC gymnastics.

I don't think the coupling argument is very strong when the effects are encoded in the type system. The kinds of effects that make sense are ambient authority operations which otherwise need explicit parameters. If they're encoded in the type system it's important that higher order functions are parameterized on possible effects to avoid unnecessarily limiting composition.

Of course all this is easier in a language with global type inference, since that will generate appropriately generic signatures that don't needlessly prevent flow of type information.

I don't think you're the only one. Like a lot of things that come from academia A, B and C are all well defined short functions where their pre and post conditions are clear. In real world code A, B and C will more likely be a mess of code written by 20 people over ten years resulting in the comment "DO NOT TOUCH".

As with all language features though, idiots will always take things too far.