Computation Containers (or let Docker mind your Ps and Qs)

The software industry is at the beginning of the container revolution. I believe that it won't be long before we are all using containers to a greater or lesser extent everyday, for a wide range of use cases. To date, uptake and discussion has mainly focussed on using Docker to host web applications and power microservice architectures, but in this post I'm going to focus on another use case, which seems reasonably common but less written about. As far as I know, there isn't a name for this use case, so I'm going to claim the term Computation Container. (I apologise in advance for being more abstract and borderline theoretical than normal in this blog; I think I must have eaten too many mince pies).

P {Q} R

If we paraphrase CAR Hoare, we can make software sound simple; program Q, when given input P, will produce output R (or, more accurately, precondition P and program Q produce result R)1. One of the major problems in modern computing is our Ps and Qs don't match when we move between environments, causing our Rs to differ unexpectedly. Docker containers are essentially just a (very nearly) complete statement of P and Q (that is to say they specify the entire state of the runtime environment (P) and the program to run (Q)). Whilst this explains why containers are so important to DevOps, it also suggests that containers can be used very effectively to define non-service, non-interactive programs which just take an input and return a result; our computation containers.

Compiler Containers

Enough theory (or Ps, Qs and Rs anyway). The most common example of a computation container is the compiler container. Here we have a container that takes source code as input and spits out an executable. For example, on any machine with Docker:

// hello.go
package main
import "fmt";
func main() {
    fmt.Println("Hello World!")
docker run --rm -v $(pwd):/usr/src/myapp -w /usr/src/myapp golang:1.4 go build hello.go
(out) Hello World!

The container took some Go source code as input, ran and spat out an executable. I didn't need to install a Go compiler or any dependencies. Instead, I just ran the container which held all my dependencies and got exactly the same output that anyone using the same image (and input) will have.

Compiler containers allow developers on distributed teams to agree on a compiler container that includes the exact version of the compiler and all dependencies to be used in the project, avoiding a whole range of niggly issues and installation problems. This pattern avoids the need to ship compilers and development dependencies in containers hosting executables - you use the compiler container to produce the executable which gets shipped in a separate container. Another advantage is just playing with unusual languages, for example it's possible to get a working Idris container in a few seconds, saving the pain of installing Haskell and building from source.

More Computation Containers

An important point about computation containers is they are ephemeral - they take some input, run, produce some output and go away again.

Some other notable use cases for computation containers:

    • Website generation; containers hosting static site generators such as Jekyll can take markdown etc as input and output HTML ready for hosting in a web server.
    • Format conversion in general e.g. this latex to PDF container.
    • Longer lived computation such as raytracing, password cracking and prime number generation.

Computation containers may be less sexy than their microservice brethren - they are normally short-lived and often exist only on the user's local computer - but they may well turn out to be an important step in making reliable and repeatable software available across environments.

1. This is equally true at the level of functions/methods and even single statements. Precondition P needs to include all state that is referenced (indirectly or directly). If you understand what this means in your typical Java or Javascript program, you can also appreciate the advantages pure functions and immutable datatypes bring. To understand more about program proof, see the original CAR Hoare paper An Axiomatic Basis for Computer Programming.

Leave your Comment