Programming Languages And Operating Systems
An operating system is an abstraction layer over hardware that provides a computational model. A programming language is an abstraction layer over hardware that provides a computational model. These are the same thing.
Both have schedulers. Both have process models. Both have memory models, IPC mechanisms, sandboxing, capability systems. The shell is the programming language of Unix, its front-end language that directly maps to the core mechanisms. The conventional distinction between “operating system” and “programming language” doesn’t really make sense, they are the same thing.
Programming Languages are Just Impoverished Operating Systems
Programming languages are operating systems that lack two important features:
Code as data. In Unix, an executable is just bytes. gcc is a
normal program that lives inside the model it compiles for. In
JavaScript, new Function is a magic built-in, the compiler lives
outside the paradigm. You cannot serialize a closure and send it across
the wire, but you can send an ELF binary.
No seam between computation and storage. In Unix, live data and persisted data share the same model. A process and a file are not different kinds of things, they are the same kind of thing in different states. In a programming language, your native data structures and your storage format are different things. You serialize to get data out, you deserialize to get it back. The seam is always there.
Consequences
Here are some things that you do every day as a direct consequence of these two missing features:
Builds (npm, pip, nix, gcc): code-as-data requires an entire external toolchain to manage.
Databases: because your data model and your storage format are different things, so you need an external system and a compatibility layer (SQL, ORMs).
Migrations: every time your data model changes, a complex ceremony is necessary to reconcile it with the storage model.
Git: your code should just be a data structure in your system; editing, builds, and upgrades should be features of the system itself. Instead all of this lives outside, managed by external tools and services.
A programming system with these two features becomes an operating system. Much of this complexity can go away: Deployment just is IPC, builds and migrations are just functions, version history is just a data structure.