Comment by chombier

7 years ago

Near the end of the article:

> Because algebraic effects are coming from statically typed languages, much of the debate about them centers on the ways they can be expressed in types. This is no doubt important but can also make it challenging to grasp the concept. That’s why this article doesn’t talk about types at all.

I'm only remotely familiar with algebraic effects, but I thought the whole point was to have a nice & composable way of dealing with effects in the type system, as an alternative to monads that generally do not mix well.

Also, Daan Leijen's papers on Koka are pretty accessible.

In e.g. Haskell, you often Dont explicitly write out types, letting the compiler infer them for you. This could essentially cascade all the way up.

Meanwhile, if you want to add logging in Haskell via a monad, you don't just need to change the type of each calling function to make it monadic, but you need to rewrite the function to make it monadic. That is harder work than changing some types. Moreover it is harder to automate by an IDE.

  • 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 about apparently straight through local code in order to gain the benefits (imagine you check a condition that your later code relies on and then call a log function that unbeknownst to you happens to use effects and doesn't return control until much later when the condition no longer holds?) Can we provide timeouts to effects? Get progress updates? Where should we use effects and where should we use dependency injection? Can library code detect whether handlers are installed for the effects it might want to use up front and fail fast or provide defaults if they aren't there? Will our debuggers understand? What are the practical best practices to avoid the kind of insane spaghetti that this seems to invite?

    • 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 inject a service, the types prevent it.

      1 reply →

  • Logging is probably easier, since a tuple with the output and the logging naturally forms a monad. What is really tricky o pull off is allowing interaction with the environment, which basically requires a more fine grained (and customizable) version of the IO monad.

    Monads have the problem that they don't commute, which is usually not what you want for handling events (if you've got handlers for two different types of event it shouldn't matter which event handler was registered first) Maybe the algebraic effects solve this problem.

> 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.

What's old is new again.

  • 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.

    • Those 1970s/1980s conditioning systems specifically evolved from the inheritance-based type systems originally developed for object-oriented systems.

      Computer science is a field that notoriously ignores the past, so these folks are far from the only people to reinvent something already well developed.

      1 reply →