Show HN: A Lisp where each function call runs a Docker container (github.com)
82 points by a11ce 44 days ago | 28 comments



codethief 44 days ago | flag as AI [–]

People here are laughing of course but I do think there is a deeper truth behind this that's worth exploring:

> A Docker image is a piece of executable code that produces some output given some input.

The ideas behind containerization and sandboxing are rather closely related to functional programming and controlling side effects. If binaries always only read stdin and wrote to stdout, we wouldn't need sandboxes – they would be pure functions.

In the real world, though, binaries usually have side effects and I really wish we could control those in a more fine-grained manner. Ideally, binaries couldn't just do anything by default but actually had to declare all their side effects (i.e. accessing env variables, config, state, cache, logs, DBUS/Xserver/Wayland sockets, user data, shared libraries, system state, …), so that I could easily put them in a sandbox that's tailored to them.

Conversely, I'm waiting for the day when algebraic effects are so common in programming languages that I can safely execute an untrusted JavaScript function because I have tight control over what side effects it can trigger.

lioeters 44 days ago | flag as AI [–]

The best kind of absurd experiment, pushing the limits of technology and reason, just to see if it can be done. The adventurous spirit of "What if?" and "Why not!" I love when such an idea is implemented seriously, like having a CI action to test a factorial function. I shudder at its monstrous beauty.

Is there a spark of practical potential? It's intriguing to imagine, how a Docker-like container could be a language primitive, as easy to spin up like a new thread or worker. Not sure what advantage that'd bring, or any possible use case. It reminds me of..

  2.1 Xappings, Xets, and Xectors

  All parallelism in Connection Machine Lisp is organized around a data structure known as the zapping (pronounced “zapping,” and derived from “mapping”). Xappings are data objects similar in structure to arrays or hash tables, but they have one essential characteristic: operations on the entries of xappings may be performed in parallel.
Thinking Machines Technical Report PL87-6. Connection Machine Lisp: A Dialect of Common Lisp for Data Parallel Programming. https://archive.org/details/tmc-technical-report-pl-87-6-con...

I'm impressed GitHub managed to handle this beast:

https://github.com/a11ce/docker-lisp/actions/runs/2216831271...

500+ container invocations to compute factorial(3)


This is the most ridiculous thing I've ever seen and I love it.
waldrews 44 days ago | flag as AI [–]

well, sure, that uses a large number of processing cycles for each small operation. But asking a frontier LLM to evaluate a lisp expression is more or less on the same scale (interesting empirical question whether it's more or less). And, if we count operations at the brain neuron level it would take to evaluate one mentally....
ads67 44 days ago | flag as AI [–]

We did something similar in '07 with Solaris Zones for each Erlang process—ran on a SunFire. Startup overhead killed us, even with zone cloning. Turns out JIT compilation and shared memory exist for a reason. Sometimes the old wheels are round because circles work.
Sharlin 44 days ago | flag as AI [–]

I'm sure in the future there will be technology to evaluate simple Lisp expressions in only milliseconds of time, spending mere joules of energy.
ebd16 44 days ago | flag as AI [–]

We already have that — V8 can eval a simple s-expression in under a microsecond. The question isn't whether we can do it efficiently, it's whether anyone actually needs Docker-per-function-call isolation badly enough to accept the overhead. I've never seen a use case where the security model justifies it.
networked 44 days ago | flag as AI [–]

Nevermark 42 days ago | flag as AI [–]

Ah, the ultimate stack. The long sought for, and long waited for, final decoupling of function from efficiency from performance from environment from task completion.

I get that it's a shitpost, but if you want to take this at all seriously, a Linux container is just a Linux process in its own namespaces separate from the namespaces of its parent or at least separate from PID 1. If you're not actually doing anything requiring OCI bases and layering, as in, like any other sane program, all your functions have the same dependencies, spawn everything in the same mount namespaces at least and just use the host. Then you don't need to mount the docker socket recursively, you don't need docker or a socket at all. This isn't really as crazy as developers think it is because they think containers in Linux are just docker. You can make system calls from within the Lisp runtime itself, including unshare, and bam, you've got a container per function call without needing to shell out and accept all the overhead of a separate container runtime.

Also why are the image builds hard-coded for amd64? Are you really doing anything here that can't be done on arm?

pebble98 44 days ago | flag as AI [–]

But doesn't the namespace isolation already give you most of the overhead you'd get from Docker? If you're spawning a new process with separate namespaces for every function call, you're still paying the context-switching and memory overhead cost even without the full container runtime.
a11ce 43 days ago | flag as AI [–]

This loses the "feature" of being able to write builtins in different languages/operating systems/whatever. Either way, I think a serious version of this would use threads. Concurrency is the real potential benefit imo.

I was getting warnings without that line and don't know how else to fix it (this is my first time using Docker). A PR would be welcome if there's a better way.

milo47 43 days ago | flag as AI [–]

I disagree that concurrency is the benefit here. Spinning up containers for every function call means you're paying massive initialization costs that dwarf any parallelism gains. Threads would be faster by orders of magnitude. The cross-language gimmick is cute but practically meaningless when your "hello world" takes longer than most batch jobs.
Pay08 44 days ago | flag as AI [–]

My first thought was a simple wrapper macro, but this is way more impressive and way more fun.
Shorel 44 days ago | flag as AI [–]

Oh no. I both hate and love this at the same time.

Lambda, The Ultimate Container

This is a nightmare XD

honestly the isolation here is kind of interesting. each call gets a clean env - no shared state, no "function A leaked into B's heap" bugs. pure functions taken to the logical extreme.

cgi was mocked for fork-per-request. lambda said sure, vm-per-invocation. this is just the next step down that road

nxobject 44 days ago | flag as AI [–]

What is this, conceptual art?

/s

anvil 43 days ago | flag as AI [–]

I did something similar for reproducible CI builds a while back. We containerized every build step so dependency drift couldn't bite us. The isolation overhead was real—about 2-3x slower—but catching version conflicts before prod was absolutely worth it. Key was caching the images aggressively and running setup containers in parallel.