Why abstract interface for effects matters
- generator functions
- async functions
- async generator functions
The latest extension, namely async generators, isn’t without problems. There is no way now to cancel async computation in JS now. This means it is impossible to combine async iterators as easily as Observables without introducing leaks. There is a proposal to add the cancellation, but it is still on too early stage and will probably take years. Alternatively, you may use Effectful.js immediately without waiting.
Libraries should be ready for abstract interface too. For example, higher order functions, like Array traversal functions (
Array#forEach, etc.). Say, we have a function duplicating elements implemented as a simple generator function:
We cannot just copy-paste the body of the loop into, say, forEach body like this:
To make a fully general library we need four versions of
Array#forEach, and each other higher order function:
Array#filter, etc. Say, filter predicate may need to consult some remote service and so it should be async.
It is even worse if the task like AST transforms with a significant number of nodes and Visitors written for traversal. To make a generic library there should be four versions of visitor types again.
There is an abstract interface (Monad) for effects. Only one implementation for the functions above is needed if it uses such abstract interface to build result.
The Monad interface came from math, namely category theory. Its only purpose was an abstraction. Theorems proved using abstract monads interface may be immediately applied to likely also abstract but more concrete interface instances, like some structures from universal algebra or topology. Mathematicians refactored math frameworks by abstracting common things into Category Theory and Monad as a part of it.
Later the interface was utilized to describe and reason about programming languages with effects in domains theory. The same purpose again, simplifying reasoning about programming languages and programs.
After, Monads reached practical programming languages. Other researchers worked on programming languages with only pure functions to simplify reasoning about programs. Unfortunately having only pure function requires threading functions parameters and results, result statuses, etc. This makes programs very verbose and hard to read. The problem was solved by applying Monads. Imperative code with a mutable state, exceptions can be converted into a pure function.
Applying Curry–Howard correspondence, where programming language types are theorems, and programs are their proofs, Monads are abstract API. So like in math, proved theorems with some general math structure may be applied to any concrete realization of that structure. In programming languages, a function using abstract type for arguments objects may be invoked with any concrete implementation of that object.
Many custom effects or changes/fixes for ECMAScript can be applied immediately without awaiting committee for years. For example:
- cancelation to async functions, thus making async iterators compasable
- persistent state (time traveling, distributed apps, workflows)
- non-deterministic programming (bind form’s data as logical formulas, reactive programming)