The influence of Self

November 1, 2022

Self is a prototype-based dynamic object-oriented programming language, environment, and virtual machine centered around the principles of simplicity, uniformity, concreteness, and liveness.

I’ve been reading a bunch of old papers from the Self project. It’s pretty incredible how many things they invented that are absolutely essential in modern, high-performance managed language runtimes. It might be the most influential programming language that most programmers have never heard of. If you work in JavaScript, Java, or any JVM or .NET language — you’re benefitting from ideas pioneered in Self.

I’ve found nearly all of the Self publications to be well-written and easy to understand. If you’re interested in programming language implementation (VMs, JITs, etc.), the ones listed here are definitely worth your time.

But the performance techniques are just a small part of what makes Self so interesting. For a good overview, I’d recommend Dave Ungar’s 2009 talk Self and Self: Whys and Wherefores.

Self: The Power of Simplicity (1987)

This paper is a great starting point, covering the core of the language and the philosophy behind it. It’s also probably one of the best PL systems papers ever published.

Class-based systems vs. Self

An Efficient Implementation of Self, a Dynamically-Typed Object-Oriented Language Based on Prototypes (1989)

It’s hard to imagine a PL paper that starts stronger than this:

We have developed and implemented techniques that double the performance of dynamically-typed object oriented languages. Our Self implementation runs twice as fast as the fastest Smalltalk implementation, despite Self’s lack of classes and explicit variables.

At the time, the benchmark for dynamic OO languages was ParcPlace Smalltalk — the so-called “Deutsch-Schiffman system”1. They had pioneered the use of dynamic translation (aka just-in-time compilation), as well as inline caching to reduce method dispatch cost.

The Self paper introduces two new ideas:

  1. Maps, a concept that’s better known today as hidden classes. This is an extremely important idea that made nearly all the other optimizations in Self possible. For example, an inline cache relies on a “shape check” to determine a hit or miss. In Smalltalk, you check the receiver’s class — in Self, you use the receiver’s map.
  2. Customized compilation, or “type specialization”:

    The Deutsch-Schiffman Smalltalk system compiles a single machine code method for a given source codemethod. Our SELF compiler […] compiles a different machine code method for each type of receiver that runs a given source method. The advantage of this approach is that the compiler can know the type of the receiver of the message at compile-time, and can generate much better code for each of the specific versions of a method than it could for a single general-purpose compiled method.

Optimizing Dynamically-Typed Object-Oriented Languages With Polymorphic Inline Caches (1991)

This paper introduced the polymorphic inline cache, an extension of the (monomorphic) inline cache used by Deutsch & Schiffman in Smalltalk:

Polymorphic inline caches (PICs) provide a new way to reduce the overhead of polymorphic message sends by extending inline caches to include more than one cached lookup result per call site.

It also covers the use of PICs as a source of type information for an optimizing compiler, and outlines some basic strategies for adaptive recompilation:

As an important side effect, PICs collect type information by recording all of the receiver types actually used at a given call site. The compiler can exploit this type information to generate better code when recompiling a method.

A Third-Generation Self Implementation: Reconciling Responsiveness with Performance (1996)

This paper is a basically shorter version of Urs Hölzle’s 1994 PhD thesis, which introduced type feedback and adaptive optimization:

Type feedback allows any dynamically dispatched call to be inlined and substantially mitigates the performance penalty of dynamic dispatch. In our example implementation for the dynamically typed object-oriented language Self, type feedback reduces the call frequency by a factor of four and improves performance by 70% compared to a system without type feedback.

Adaptive optimization discovers and optimizes the “hot spots” of a program while it is running. A method is compiled on-demand by a fast and dumb compiler, and the result is instrumented and cached. Only if a method is executed often is it recompiled with an optimizing compiler.

Most of these ideas would later make their way into Java, though not by the most direct route. By 1991, Dave Ungar and Randy Smith — the founders of the Self project — were working on Self at Sun Microsystems. In 1994, Urs Hölzle and Lars Bak, two of the key Self engineers, co-founded a company called Animorphic Systems to work on Strongtalk. They later pivoted to Java, and in 1997 Animorphic was acquired by Sun, where their technology became the basis of the HotSpot engine.2

Surprisingly, even though Self was a direct inspiration for JavaScript3, it took much longer for JS engines to adopt these techniques. Lars Bak was of course hired by Google to build V8, which was released in 2008. Within a few years, most of the techniques pioneered in Self had made their way into the major JS engines.

👉 You might also want to check out the discussion on Hacker News.

  1. See Efficient Implementation of the Smalltalk-80 System

  2. See The History of the Strongtalk Project

  3. From JavaScript: The First 20 Years:

    After joining Netscape, he [Brendan Eich] had explored “easy to use” or “pedagogical” languages, including HyperTalk, Logo, and Self. Everyone agreed that Mocha would be “object-based,” but without classes, because supporting classes would take too long and risk competing with Java. Out of admiration of Self, Eich chose to start with a dynamic object model using delegationg with a single prototype link.