Live data from Hacker News

Algebraic Effects for the Rest of Us

overreacted.io

101–106 of 106 posts

Re: Algebraic Effects for the Rest of Us

#101
post #91

Earlier quoted context omitted.

> Near the end of the article: >> Because algebraic effects are coming from statically typed languages, Though in fact the origins come from dynamically typed languages like Lisp, where condition signalling systems were developed in the 1970s. When they started appearing in statically typed languages in the late 80s, they were only used for errors, and by the time they were caught the stack had already been unwound.…

Sure but I mean most work on algebraic effects (or at least the papers I know of) deals with the associated type system . Instead most people here seem to focus on the semantics, which indeed boil down to call/cc + handlers or previous condition systems.

Well, a reason to care about effects systems is to have a way to specify what a program is doing in a way that lets you calculate and manipulate properties of it (from proofs to optimisations), which generally means static typing and purity.

The curry-howard equivalence is only as useful as the strength of the type system. We ideally want proof irrelevance, that is, any program satisfying the types is sufficient, but while some things remain outside the type system this is only true up to a point (think: time and space costs). Putting effects into types helps this along.

Re: Algebraic Effects for the Rest of Us

#102

Earlier quoted context omitted.

That explains why one of his examples is a log handler, which in javascript would be sensibly provided by dependency injection, but that doesn't work as well for haskell. Things I'd want more information on: what about errors in effect handlers (or would errors be reimplemented as effects?), effects with no handler, which handlers do effects inside handlers use? Is it really worth making it much harder to reason abou…

Dependency injection works fine on Haskell. An effect system is one way of implementing it. That is, you write code that says it may perform any effects from the following list. At some central point, you execute that code in the context of a handler for those effects. That's the same idea as using dependency injection to provide a service. The only difference is that the ergonomics are better. You can't forget to in…

You're right, this is an interesting way to implement dependency injection.

A big problem this approach solves is that it avoids code that relies on injected code from needing to predict in advance all the things that injected code might need to do. This problem is much less pronounced in a dynamically typed language (since you haven't had to predict the type signature of the injected code).

In javascript the most significant time this is a problem is when the injected code might need to be asynchronous. In standard javascript, if that's the case, the surrounding code needs to know about it.

In Haskell, you have the same problem doing something as simple as debug logging.

My concern is just that adding effects reduces how much you know about what a particular function does just by looking at it, and this is true in both typed and untyped languages.

Re: Algebraic Effects for the Rest of Us

#103
post #79

The author ought to look into and write about the Common Lisp condition system, which allows error handlers to invoke restarts at different parts of the call stack. [1] The long-story-short on them is that they decouple the treatment of exceptional situations (or conditions ) into three orthogonal roles: signaling the condition (akin to “throwing”), handling the condition (akin to “catching”), and recovering from the…

Algebraic effects can actually be implemented with delimited continuations [0]. Algebraic effects are more aimed towards statically typed languages like Haskell or Ocaml. They can replace most uses of monad transformers, which has both cognitive and performance benefits. As you showed, there are better ways of solving the problem in lisps. Tagless final algebras are another much more popular alternative that has been…

Algebraic effects and delimited continuations are strictly more powerful than the Common Lisp condition system which is largely powered by the lexical goto (or return-from) feature. Invoking a restart can only unwind the stack (and the bit which is unwound can never be gotten to again), whereas algebraic effects allow more of a “forking” behaviour.

Re: Algebraic Effects for the Rest of Us

#104
post #59

I, like many people in this thread, learnt about algebraic effects for the first time from the posted article. However, many commenters seem to be mislead by the explanation based on an example with exceptions. What I learnt from [1] linked below is that algebraic effects is a generalization of which language constructs like try/catch, async/await, or generators are just particular cases. From that perspective, algeb…

Algebraic effects are synchronous; they can't be a generalization of async anything, because that uses threads.

A generalization has to do everything that the specialization does, like dispatch on multiple processors.

The synchronous version of async/await is delay/force; that is just macrology over some lambdas.

Re: Algebraic Effects for the Rest of Us

#105

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 archi…

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.

Re: Algebraic Effects for the Rest of Us

#106
post #59

I, like many people in this thread, learnt about algebraic effects for the first time from the posted article. However, many commenters seem to be mislead by the explanation based on an example with exceptions. What I learnt from [1] linked below is that algebraic effects is a generalization of which language constructs like try/catch, async/await, or generators are just particular cases. From that perspective, algeb…

Algebraic effects are synchronous; they can't be a generalization of async anything, because that uses threads. A generalization has to do everything that the specialization does, like dispatch on multiple processors. The synchronous version of async/await is delay/force; that is just macrology over some lambdas.

Not all implementations of async are thread-based. For instance, in C# it's task-based. In F# it's thread-based.
Post reply on HN