Much of Computer Science research never materializes in the practical, real world of PHP pages, Bash scripts and moving CSV files over FTP. Some, though, ends up re-discovered and in daily use ever since. This is one such story.
What did the future look like back then? What has changed in those 25 years? And what can we learn to save us from yet another delayed re-discovery?
Maps and prototypes
The underlying data structures in Self are maps and prototypes:
A computation in Self consists solely of objects which in turn consist of slots. A slot has a name and a value. Slot names are always strings, but slot values can be any Self object. A slot can be marked with an asterisk to show that it designates a parent. … When sending a message, if no slot name matches within the receiving object, its parent’s slots are searched, and then slots in the parent’s parent, and so on.
Maps (or their concrete type hash-table) have been a fundamental data structures since before the epoch. They’re used by every object-oriented language, but many languages complicate and then hide them underneath a pile of higher level abstractions. Try the Java Reflection API, or enumerating object/class methods/variables associated with a Ruby object, you’ll see what I mean.
I’d go further and claim in recent years maps have made the leap from blue-collar data structure into a key architecture pattern. Maps are the basis for exchanging information, solving big data storage and coordinating large-scale distributed systems.
I’m hoping prototypes will emerge as fundamental, though we’re not there yet. I guess it’s the combination of developers still thinking in terms of Class-Inheritance coupling, and not enough examples that put prototypes to good use.
Self is a language rooted in its environment, and the Self environment is about exploring and directly manipulating objects. It’s called “Live editing”:
Live editing is partly a result of having an interactive system, but is enhanced by features in the user interface. This principle reinforces the feel that the programmer is working directly with concrete objects. The following example will clarify how this principle and the structural reification principle help give the programmer a feeling of a working in a uniform world of accessible, tangible objects.
It is important to note that during this whole process, the simulation could be left running — there was no fundamental need to enter an “edit” mode, or even stop the atoms from bouncing around. The live editing principle makes the system feel responsive, and is reminiscent of the physical world’s concrete presence. Structural reification means that the parts of the interface are visible and accessible for direct modification.
Sounds familiar? If you’re reading this on Chrome or Safari, pick any element on the page and right click on Inspect Element. You can manipulate any object, change its properties, bind new methods, all without stopping scripts from executing.
I heard people complain of a mismatch between DOM and a proper abstraction for composing interactive UIs. Then again, what other environments makes it possible for every user to reach into the UI and manipulate at will? Can you think of a better way to allow infinite customization and learning by experience?
This, direct manipulation without layers of indirection and complex tooling, is how we get more people to program.
Minimalism of expression
Why have we tried to keep the Self language minimal? It is always tempting to add a new feature that handles some example better. Although the feature had made it possible to directly handle some examples, the burden it imposed in all reasoning about programs was just too much. … Although adding features seems good, every new concept burdens every programmer who comes into contact with the language.
We have learned the hard way that smaller is better and that examples can be deceptive. … We now believe that when features, rules, or elaborations are motivated by particular examples, it is a good bet that their addition will be a mistake. The second author once coined the term “architect’s trap” for something similar in the field of computer architecture; this phenomenon might be called “the language designer’s trap.”
The Architect’s Trap. Learn to avoid it.
It turns out there are far more valuable traits than “every possible feature”:
If examples cannot be trusted, what do we think should motivate the language designer? Consistency and malleability. When there is only one way of doing things, it is easier to modify and reuse code. When code is reused, programs are easier to change and most importantly, shrink. When a program shrinks its construction and maintenance requires fewer people which allows for more opportunities for reuse to be found. Consistency leads to reuse, reuse leads to conciseness, conciseness leads to understanding. That is why we feel that it is hard to justify any type system that impedes reusability; the resultant duplication leads to a bigger program that is then harder to understand and to get right. Such type systems can be self-defeating.
The lesson here is not just for language designers. Consistency and malleability will make your API/library better, it will help you build frameworks and modularize large apps, expose and consume remote services.
Optimize for people, not machines
Minimal languages lack declarations, whether types, annotations or other compiler performance crutches:
Self’s design departs significantly from other object-oriented languages by separating information needed to run the program from information about the programmer’s intentions. It distinguishes abstract types, used for the programmers understanding and reasoning about correctness, from concrete types, used to run and optimize the program. The former is left to the environment, the latter is left to the implementation. In our opinion, this approach avoids a number of undesirable consequences that often follow from attempts to integrate these two forms of information.
Self tackles performance by coupling an interpreter and an optimizing compiler with type-feedback, and switching between the two as necessary. Sounds familiar? You might be thinking of Chrome’s V8 Crankshaft compiler.
Why go to all that trouble?
Along the way Self’s implementation techniques of adaptive recompilation and type-feedback achieve some traditionally-important but rarely achieved goals as well: the elimination of run-time penalty for factoring and for user-defined control structures. A programmer may chop up a method as finely as desired without slowing it down, and may introduce new abstractions that combine control and data without paying a run-time price. These characteristics encourage the create of programmers that are smaller and more malleable.
25 years later, it’s part and parcel of every modern browser.
Minimalism, simplicity and consistency are better guides. They benefit every programmer, not just the ones who need advanced features. We suspect that many of today’s object-oriented languages could profit by dropping features.