Effects As Data

Philip Borlin
3 min readMar 23, 2018

As developers we are both familiar and comfortable with the separation between writing code and executing code. We can open a text editor and write some code, but in most languages there is no expectation that the act of writing code is going to do anything. We have a separate process which is going to execute the code. Some languages choose to require a compile step (Java, C#, Go, Haskell, etc…) before execution and some will allow direct execution (Javascript, Python, Ruby, etc…), but in all those cases there is a hard barrier between coding and executing the code.

Extending the Abstraction

What if we added another interpreter into our layers of abstraction? What would that do?

This isn’t such a crazy idea.

Java has been doing this for years. Java has Servlet Containers, EJB Containers, and OSGi. .Net has IIS which has similar properties to various Java containers (not to be confused with Docker containers). There are plenty of examples of having code manage the lifecycle of other code.

With effects as data we are going to use a very specific approach. We are going to create a number of effects stack (a first in first out (FIFO) stack — so really a queue, the name isn’t my fault) that we are later going to run through interpreters.

The effects we are going to put into our stack will be a made up language that we are going to create (an effects as data framework may come with some commonly used effects). This language will be a domain specific language (DSL) which will contain commands that are useful for creating a solution to the problem we are creating solfware to solve.

As consistent with DSL programming we are going to first develop a language that makes solving our problem easy and then we are going to write our solution in that language. It is much easier than it seems.

This new language will be run through an interpreter we have either written in our normal programming language or which has been provided for us by the framework. Many frameworks ask us to write a handler for every command in our DSL. Some frameworks allow us to setup different interpreters for different situtations. Imagine having one interpreter for production and a different one for testing. Instead of mocking we can write a test handler that has predictable behavior suitable for testing.

Benefits

There is a place for optimizations. On a long running session an interpreter can learn about the program that it is running and make optimizations to the way it runs the program.

There is an place to add telemetry data that is automatically collected and does not to be handled within the code itself. Think of all the things aspect oriented programming promised and now you have a place to put those things.

A concreted way to implement and use DSLs.

Side Effects happen in handlers, not in your DSL. You get to experience the joy of side effect free development when using your commands.

Testing becomes easy. You can test each command/handler pair isolation and use alternate handlers instead of mocking. Writing a special handler for a test feels super natural.

Trying it

There are three good places to explore this concept.

Elm is a server side language that was built with the effects as data paradigm built into the language.

Effects As Data is the best Javascript framework you have never heard about. It is written by a talented developer named Frankie O’Rourke and was built to be very useable.

Free Monads are a hard core functional implementation of these ideas. Several languages have Free Monad libraries. This is the least accessible, but most powerful option of the three.

Conclusion

Effects as Data allows you to write a DSL which will be run through an in-code interpreter. This interpreter can add value during run time and make testing easier. There are many implementations and the more accessible ones are pretty easy to get started with.

--

--