Environment and the programming language Self

Environment and the programming language Self

Foreword

Part one; environment

Part two; language

Part three; debugger, transporter and problems

Part four; community, history, future and metaphysics

This book was converted from the HTML articles originally published at my blog blog.rfox.eu.

I want to apologize for the low quality of the export. If you feel like you want to improve the quality, your contribution is most welcomed:

https://github.com/Bystroushaak/Environment_and_programming_language_self_epub_generator

I bring you a message about a language that has been at the birth of many others, but almost no one knows it. A rumor of a graphical environment offering options like no other, but almost no one using it. I also bring information about a virtual machine supporting reflection almost to infinity, reaching almost half of the operating power of C in numerical calculations in its time, yet today forgotten.

Today, I'll tell you about the Self. Different people said different things about Self:

Self is (like) Smalltalk, just more.

Self is the sea of objects through which flow flashes of messages.

The self is so advanced idea of the future that despite being twenty-five-year-old, it is still more than just inspirational. Visionary is the right word for it.

I liked Self. “Good OOP” is still waiting for a much better notion to replace the idea of a “Class”

– Alan Kay, https://news.ycombinator.com/item?id=11939851#11941380

The only other language of promise is Self, and that is unfunded and locked in non-development though so incredibly influential.

— Interview with Alan Kay

Software archeology in practice. A rich treasure nobody knows about and nobody uses. Hundreds of thousands of lines of forgotten code, ideas, concepts.

Self is many things, so we will look at it from many different points of views.

From a technical point of view

Self is a programming language, bytecode interpreter and system of memory "images", which are executed by an interpreter.

From practical point of view, it is a binary with a virtual machine and separate a memory image file in which objects forming the environment are stored. The situation is somewhat similar to common scripting languages, but instead of the script, you run the memory image in the state that the script last ended.

The memory image contains the entire development environment and its own graphical interface with Xlib bindings.

There are also compilers for the programming language, the entire standard library, and so on.

Programming language

The Self programming language is a prototype-based, object-oriented Smalltalk-like language.

Prototype-based means that it does not use classes to create objects. New objects are created either by copying or by creating a new object from the source code at the syntactic level.

Smalltalk-like means that everything revolves around sending messages to objects. Apart from a few basic syntactic sugars, you will find nothing more than messages sent back and forth.

As a language, Self is very simple to learn. Virtually all of the features of this programming language fit on a single A4 page with a few labels and explanations.

That will be all for a brief overview. Further details will be given in the following chapters.

Interpreter

You can download the Interpreter and the basic image from the official website http://www.selflanguage.org/. There are binary distributions available for GNU / Linux and Mac OS X. There are also source codes released under OpenSource license, that you can theoretically compile on any Unix system.

It will probably not be so easy, because of the bindings to the graphics server and the JIT compilers, that need to be tweaked to a particular processor family.

After downloading and unpacking the archive, waiting for you are the two main files among others. The main ones are:

Self

morphic.snap

The first file is the executable binary interpreter, the second file is the memory image in which the preprepared graphical interface environment is stored.

You can start the interpreter with -h parameter to show help:

$ ./Self -h
./Self: usage: ./Self [-f filename] [-h] [-s snapshot] ...
Options:
  -f filenameReads filename (Self source) immediately after startup
  -hPrints this message
  -pDon't do `snapshotAction postRead' after reading snapshot
  -s snapshotReads initial world from snapshot
  -wSuppress warnings about optimized code being discarded
For debugging use only:
  -FDiscards saved code from snapshot
  -l logfilewrite spy log to logfile
  -rDisable real timer
  -tDisable all timers
  -cUse real timer instead of CPU timer (for OS X)
  -oOversample the timers (Run them 10x faster to flush out bugs)
  -aTest the Assembler (added for Intel)
Other command line switches may be interpreted by the Self world

32-bit system

If instead of the nelp you see something like this

./Self: error while loading shared libraries: libX11.so.6: cannot open shared object file: No such file or directory

then you are using a 64-bit system, that does not have 32-bit support installed. You can install the necessary support on Debian based systems with following command:

sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libx11-6:i386 libxext6:i386 libtinfo5:i386

Back to the Help

Only two switches are in fact interesting; -f, to load a .self file (which you probably will not use) and -s, which loads the stored memory image. The rest are mostly technical details suitable only for very advanced users.

If you run the Self binary alone, REPL (interactive mode) appears in front of you, where you can type commands:

$ ./Self
Self Virtual Machine Version 2017.1/13, Tue 16 May 17 00:45:42 Linux i386 (4.5.0-205-gd942ba2-dirty)
Copyright 1989-2016 AUTHORS (type _Credits for credits)

for I386:  LogVMMessages = true
for I386:  PrintScriptName  = true
for I386:  Inline = true
for I386:  SICDeferUncommonBranches = false (not implemented)
for I386:  SICReplaceOnStack = false (not implemented)
for I386:  SaveOutgoingArgumentsOfPatchedFrames = true
VM#

Since Self as an interpreter commonly operates at the image level, and I haven't loaded any, I am now in the interpreter where, apart from a few built-in primitives, there is essentially nothing. No standard library, almost no objects to send messages to. Self in this state doesn't even know how to add up two numbers, because it doesn't really know what numbers mean.

Basic help is available by sending a message (details below) help:

VM# help
'
To begin using Self, you must read in the world of Self objects.
To read in the world, type:

'worldBuilder.self' _RunScript

When this process is complete, you will be at the Self prompt.
At the Self prompt, you can start the user interface by typing:

desktop open

' <0>: ( | parent* = <1>. | byte array: {10, 9, 84, 111, 32, 98, 101, 103, 105, 110, 32, 117, 115, 105, 110, 103, 32, 83, 101, 108, ... (258 more elements) } )
VM#

However, the help (in the form of a returned string) is not very useful, the only thing that will show us is how to load the world from .self scripts by sending a message _RunScript to the string with the path to the script.

This is useful when we want to produce a new, "clean" image from serialized source codes. This is achieved by the gradual creation of thousands of objects forming a standard library by using several built-in primitives. It can also be seen as building a system from several axioms.

Image

The morphic.snap file contains already assembled "world" made of linked objects, which together make a standard library and other useful applications. It also includes its own user interface, which is implemented completely in the Self, with several backends per graphics server (Xlib for Unix / Linux, Quartz for OS X).

However, Self does not force you to use this image. You can have an image where there is no graphical interface, where the numbers are not loaded, or where only the webserver is needed. Although it is possible to run a large number of applications in one image, I use several separate images myself for practical reasons.

Overview of the basics of the environment

Before going into the description of the language, I would like to first explain the basics of the user interface and the Self development environment.

When you start Self for the first time, you'll see a white screen, some sort of desktop. I can imagine that for many it may be a shock. When I first turned it on, I closed it again after about thirty seconds of confused looks and mouse clicks.

The terse empty white space just doesn't encourage you to explore what you've come across.

First Shock: Mouse Oriented Environment

The thing that is hard to accept from today's point of view is that, just as the graphical interface of the Plan9 operating system, the Self arose at a time of great enthusiasm for the new periphery: the mouse. The creators promised great intuitive controls, so they decided to make the whole environment manageable primarily by the mouse.

Believe me, if you think you know what to expect from classic graphical applications, you have no idea. In Self, the mouse is not only the primary control element, you literally don't ever have to touch the keyboard, except to write the code. And even programming can to a certain extent be done by the mouse.

If you click on the desktop, both the left and right buttons do nothing and the middle calls up the menu.

From this menu, you can (of course using the mouse), select the basic tools and you can also save the memory image, or stop the interpreter.

Second Shock: Programming Language

I was wondering how to explain this for a long time. The entire graphical interface IS the programming language. The programming language is controllable from a graphical interface, and things that happen in the programming language are often displayed graphically.

The rectangle at the top right with a sign saying shell is like a terminal to write commands in. At the same time, it is also a graphic representation of the programming language object, the so-called outliner.

The arrow in the left corner allows you to view individual slots - elements in an object.

As you can see, there are db*, shortcuts*, and the help slots in this object. The first two are the so-called parent slots, that is, slots referring to the objects to which the messages not found in current object are delegated.

A message with the name of the slot can be sent to object and it returns its content. The message can also be sent graphically, and it returns the content graphically (as shown below).

The three dots next to the object name allow you to view the object's documentation comment.

This is the docstring assigned upon the object's creation.

Furthermore, we see a series of buttons in the right corner that appear a little strange since I don't have the expected fonts installed. The button labeled /\ displays parent slots. When I click on it, parent slot is put "in hand" at the cursor position and I can put it on the desktop with the next click.

That it is actually a parent slot of this object can be shown by clicking on the boxes on the right of the parent slots:

The important button is E, which is displayed for all outliners. This will display the input field, which you can use to send messages to the object.

Here you can see the help message sent by clicking below on Get it, or by pressing CTRL+Enter. This tells the Shell to give me an outliner for the string object. I could also press Do it, which would cause the message to be executed and the return value thrown away.

Te returned outliner can, of course, be further explored:

You can send messages to the returned object, reference it from other objects, and so on.

It is worth noting that the whole environment behaves like a gigantic 2D surface, where it is possible to scroll and jump using WIN+arrow.

Third Shock: User Interface Paradigm

The user interface may seem ugly and strange from today's perspective. It is, however, necessary to add in that it was created in the last century and that virtually no one has ever touched it since.

However, this does not mean, that there is nothing to offer. On the contrary, it is a consistent system based on well-thought-out principles, which include (among others):

Concreteness

Uniformity

Directness

Liveliness

Concreteness

If an outliner is displayed on the screen for a particular object, other objects also use it.

A nice example is if you view an outliner for nil:

In the example, you can nicely see how two slots point to one particular object. The outliner for a particular object should not be on the screen more than once.

Uniformity

Once you understand the basics of Morphic (the framework in which the interface is made), everything else can be intuitively derived.

For example, a letter is an object. A line of letters is composed of a string of objects in a layout. The line itself is in the layout that is in the window layout. Everything can be dynamically changed and modified.

Directness

Objects behave as if they were real physical objects existing in reality. For example, it is possible to remove a title from the window and place the copy in an empty space. It is not a nonsensical tweak - like the physical objects, the graphical interface in the Self is explorable and disassemblable. And, like physical objects, it can be used to construct new interfaces.

Because the language is based around the concept of prototypes and memory image, it is possible to continue using composited graphical interfaces and perhaps use it to build an application.

Liveness

Liveness is given by the fact that all objects are redrawn when modified, but also by using all sorts of animations across the entire user interface.

Animations are usually hidden in the background, for example, where the windows are moving, a animation is distorted, and gray blobs are rendered in the path, giving the motion a sense of speed and direction. These techniques were directly inspired by cartoons.

Another example of animation is when you get an error message, such as Syntax error, and just place it on the desktop. After some time, it goes off the screen by itself to avoid getting in the way.

Philosophy

Personally, I find interesting the emphasis on the ability of the user to completely explore and modify the graphical environment. The authors have been relatively successful in offering users the ability to create their own graphical interfaces in a relatively simple way, which is otherwise a thing that is virtually impossible for ordinary people with non-existent knowledge beyond Powerpoint.

If you are interested in the principles, I recommend for further reading:

Animation: from cartoons to user interface

Experiencing SELF Objects: An Object-Based Artificial Reality

Directness and Liveness in the Morphic User Interface Construction Environment

The Self-4.0 User Interface: Manifesting a System-wide Vision of Concreteness, Uniformity, and Flexibility (this contains especially nice explanation)

The thing on the screen is supposed to be the actual thing (David Ungar's and Randalla B. Smith's presentation), if you don't see the videos, you can download them directly:

http://davidungar.net/Live2013/Media/ARK video for Live Workshop-medium.m4v

http://davidungar.net/Live2013/Media/UI1 clip for Live Workshop-medium.m4v

http://davidungar.net/Live2013/Media/UI2 clip for Live Workshop-medium-1.m4v

Tools

Another noteworthy thing are the commonly used tools available in the Self Image and in the project repository.

Outliner

Outliner is that thing which let edit objects. It is basically a table view of individual methods and properties of the object, which, however, allows a few extra things:

Search objects and descendants.

Display shell, through which it is possible to send messages to the object.

Change individual object properties by dragging a reference to another outliner.

Move individual properties/methods between outliners by extracting ("yanking out") or copying and dropping them by drag & drop to another outliner.

Sample of context menu of outliner's slot:

Shell

The shell is just an outliner with pre-set parent slots. If you open a shell using E in a standard object, all messages are executed in the context of the object that the outliner represents. If it is an object without any parent slots, you do not have even the common messages, such as true and false. Shell offers you much more convenience in the form of an extended namespace.

You can call it up it by clicking the mouse wheel anywhere on the desktop and selecting New shell.

Core sampler

The Core sampler is a weirdly looking thing, which can be called up from the context menu (right-click) of almost any morph.

This at first glance a strange gun sight with a gray box allows you to use reflection on the entire morphic interface by displaying layers that make it. Simply move it over the specific element you wish to explore:

From this menu, it is possible to immediately change the properties of the individual layers of morphs, modify their layout, make copies, and so on.

Factory window

You can select the factory window tool from the menu by clicking the mouse wheel on a blank area.

It displays a list of keyboard shortcuts, but also various types of morphs ready for use. Just click and drag them to a blank area to start creating your user interface by folding them on top and into each other.

Radar

It is worth mentioning a screen around the middle left of the factory window. This is a radarView prototype, that you can simply take and drag to the desktop.

After you turn it on, it gives you a radar view of the full screen.

Morph can also be opened from shell:

desktop w addMorph: radarView
desktop w hands first addMorph: radarView

The first command displays the morph at the edge of the screen, the second command puts it at the cursor position into your "hand", so you can put it where you want.

You can also paste the command directly into the terminal from which you run Self:

Transporter

A transporter is a Self's way of recovering source code from objects in memory, that can then be versioned in standard VCS, for example with Git.

It works by annotating specific objects as part of a module that can then be extracted and filled out.

A practical example goes beyond the scope of this episode, but can be seen here: Exporting Self objects to source files using the Transporte

The exported code is somewhat illegible as it is stored as a sequence of transporter directives. Sample: http_client.self.

However, it is necessary to add, that it is serialization format, where editing by a programmer is not intended, the purpose is primarily distribution and versioning. Editing should always take place inside the Self itself by direct manipulation of objects.

Other tools

In addition to these tools, you can also find other tools in the objects/applications directory inside Self repository, including:

the Self Parser written in Self

implementation of Smalltalk

Trivial web browser

Library for sha1 calculations

Implementation of fastCGI server for providing web pages

mySQL client

and so on. The projects are usually dead and at different stages of work in progress. Some work, others look like they have been dead for a long time.

From the documentation point of view

On one hand, there is a Wikipedia, English one contains a relatively comprehensive introduction including a mini-tutorial of the language:

https://en.wikipedia.org/wiki/Self_(programming_language)

One of the few coherent sources of documentation is Self papers, an archive of scientific papers that can be found directly in the GitHub repository:

https://github.com/russellallen/self/tree/master/docs/papers

or in a more presentable form on the web:

http://bibliography.selflanguage.org

For newbies, the best source is a Self Handbook:

http://handbook.selflanguage.org

As an introduction to the graphical interface, this old tutorial I've put online on my site, is one of the few usable documents:

http://kitakitsune.org/self/self_tutorial/

If someone has a desire to read the entire maillist, I have the archive here:

https://github.com/SelfBatteries/self_mbox

there is quite a lot of information on there, but I would like to warn you that reading can take weeks at least. If you are interested in highlights, you can find them at the Self blog:

https://blog.selflanguage.org/2016/06/15/lessons-learnt-from-the-self-archive/

along with other articles:

https://blog.selflanguage.org

And that's mostly all. Everything else you can find is mostly outdated, not updated and irrelevant in this day and age.

Interesting papers

Among the more interesting papers, you can find:

Programming as an Experience: The Inspiration for Self

Parents are Shared Parts: Inheritance and Encapsulation in Self

Organizing Programs Without Classes

Animation: From Cartoons to the User Interface

Experiencing Self Objects: An Object-Based Artificial Reality

Getting Close to Objects: Object-Focused Programming Environments

The Self-4.0 User Interface: Manifesting a System-wide Vision of Concreteness, Uniformity, and Flexibility

Directness and Liveness in the Morphic User Interface Construction Environment

Sifting Out the Gold: Delivering Compact Applications From an Exploratory Object-Oriented Environment

I can suggest papers not included in the repository:

Constructing a Metacircular Virtual Machine in an Exploratory Programming Environment (DOI 10.1145/1094855.1094865)

A Simple and Unifying Approach to Subjective Objects (DOI 10.1002/(SICI)1096-9942(1996)2:3<161::AID-TAPO3>3.0.CO;2-Z)

And this was literally eye-opening:

A Simple, Symmetric, Subjective Foundation for Object-, Aspect- and Context-Oriented Programming

Korz: Simple, Symmetric, Subjective, Context-Oriented Programming (DOI 10.1145/2661136.2661147)

Last episode Environment and the programming language Self (part one; environment) introduced Self as a project, showed where to download it and you could also find some basic orientation about the usage of the morphic interface environment there. In this episode, we'll look into the language itself and also some more interesting parts of the standard library.

From the language point of view

On the syntactic level, Self is inspired by Smalltalk. It was born in the same place, in Xerox PARC, although a decade later. In Self, like in Smalltalk, everything revolves around sending messages. Unlike Smalltalk, Self introduces a literal for objects, which means that objects are as natural as strings and numbers in other languages.

You can understand the object as a key: val storage. Individual keys are called slots.

When you send a message to the object, a corresponding slot is resolved. If it contains data (object stored there doesn't contain code), it is returned. If it is a code object, a value, that is left after the evaluation of the code, is returned.

Message is sent to the object by typing its name after the object.

obj message

This is similar to C-like languages:

obj.message()

Syntax of the object

() creates an empty object. That is an object without slots, which can't react to messages.

(| a. b. |) creates object, which contains two slots called a and b. Values of both slots are set to nil.

| opens and closes section of slot definitions. Slots themselves are separated by dots. The above object is a "box" that has two drawers called a and b. These drawers can store data or a code.

Object in Self works a bit like a hashmap or a dictionary. A specific value can be stored in a specific slot. The value can be assigned during initialization:

(| a <- nil. b = nil. |)

You can see the two different types of assignments in the example. First one (<-) creates rewritable slot, the other one (=) is read only.

In fact, in the first case, there are two slots; slot a itself, and then a method a: (an assignment primitive), which writes to that slot. In the latter case, only the slot itself is created and it is thus not possible to write into it directly. This has its use for all kinds of constant values.

Each object can also contain code, which is listed after the second vertical line |:

(| a = 1 | a printLine)

The above example defines an unnamed object with a single a slot set to an object of the number 1. It also contains a code to send a printLine message to the slot a, that is, to the value in that slot.

Parent slots

Special type of slots in Self are the so-called parent slots, which cause, that messages, which are not found in the object, are delegated to objects that these slots point to.

When we send a message clone to the object,

(| p* = traits clonable |)

it returns its own copy, even though it does not have a method stored in the clone slot. However, this method is defined in the object referenced by the slot p*, or in the other objects in the parent chain, if this object also contains parent slots.

This mechanism effectively implements inheritance. If you think about it, this behavior resembles a situation where we call the ancestor's method of an object, that is inherited from traits clonable, for example in Python. Main difference is that a parent slot is just a slot, and that means it can be dynamically changed.

Parameters

| slot | definition can also contain parameters of the method. These are preceded by a colon at the beginning of the name. For example, an object:

(|
    x:Y: = (| :a. :b. | a printLine)
|)

contains one slot x:Y:, which points to the code object accepting the parameters a and b.

Alternative syntax for the object is:

(|
    x: a Y: b = (a printLine)
|)

Messages

There are three standard types of messages:

Unary, for example first

Binary, for example > a

Keyword, for example set: a or set: a And: b

Unary messages don't have parameters. Binary ones have exactly one parameter and are used for operators. Keyword messages can have any number of parameters. Unlike in Smalltalk, second and all following keywords of the multi-parameter keyword message start with a capital letter. This makes it clear where the message ends.

Message a first causes interpreter to look for a slot called first in the object a. If the first slot contains data, they are returned. If it contains object with code, it is evaluated and value of the last expression is returned.

Message a > 1 resolves into lookup of the slot >, where the code object is given a parameter 1. Slot of this type can contain only code objects, because they always have to accept one parameter.

Message x set: a And: b causes lookup of the slot set:And: in the object x. Resolved code object is given parameters a and b.

Primitive messages

Fourth type of messages is primitive calls, distinguished from the other types by the _ at the beginning of the message name:

_print

_set: s And: b

This type of messages is used only for calling primitive methods of the interpreter, that is the parts of the interpreter implemented in the C++.

The whole Self as a programming environment can be understood as a language built on axioms defined by primitives.

Self

Self as a language is called "Self" because unlike in Smalltalk, you don't have to use this keyword in front of every message sent to the object itself.

If you want to call a method print in the different method of the object, you can use:

self print

However, you can omit self and use just:

print

This is rather an interesting feature that is worth thinking about. Each identifier you type, and is not found immediately in the method namespace, is delegated to the object itself and then to all parent slots. What is actually a local namespace, when an object implicitly sends everything to itself?

Message a first from the previous chapter can be actually rewritten as a self a first message, or as a (self a) first message.

Blocks

Blocks are similar to objects, except for three differences:

They usually don't belong to the place, where they are defined, and are evaluated somewhere else.

They act as if they contained a parent slot pointing to the namespace where they were defined.

They automatically contain parent* pointing to traits block.

Thanks to these features, they work as a closures known from the other programming languages.

[] is an empty block.

Like objects, block can contain slots: [| a <- 1. |] will create a block with a rewritable slot a set to value 1.

Blocks can also have parameters: [| :a | a printLine] and act as code objects. To call a block, you can send a message to it: value, or value: if the block expects one parameter, or value: .. With: .. With: .. if it expects multiple parameters.

Blocks are used to implement all control structures used in Self: if conditions, loops and so on.

For example, if condition is just a keyword messageifTrue:, or ifTrue:False: sent to bool object, which expects block as a parameter:

(| :a. :b. |
    (a > b) ifTrue: [^a] False: [^b].
)

Here you can see the code object with two parameters a and b, which are compared to each other by sending a binary message > b to the object a and then sending the message.

The caret character ^ is used for return. If used in a block, it returns a value not only from the block itself, but also from the surrounding object. In this case, the value from the entire code-object is returned, not just from the block (this can be done using return:). So if ais greater thanb, a itself is returned from the whole code object.

Generally, with respect to return values, it is either possible to use the ^ to return a value or the value of the last message in a given code object/block.

(|
    parent* = traits boolean.
    a = (true ifTrue: [1])
|)

Here we see an object definition that contains a parent slot pointing to the traits boolean object. This makes the message true accessible.

The method stored in the a slot then sends a true message to self, which returns the content of a true slot defined by traits boolean's parent chain. A keyword message with a block parameter, that contains the code consisting just from the object 1, is then sent to this object.

Because the object 1 is the last value in the block, it is returned. The result from ifTrue: message is also returned and becomes the last value in the method stored in the a slot and is thus also returned as a value.

Result of the a message is thus the object 1, even though the return is not used.

Delegation

As I already mentioned, Self uses something that works as an inheritance, but it is not the typical inheritance as we know it from other mainstream languages. Better description would be "a delegation of messages, which the object can't resolve itself, to objects defined in parent slots."

Object can have multiple parent slots, that contain slot with same name. This leads us to so called resends, which allow us to specify which parent should be used for slot resolution. Syntactically, it is defined as <parent name>.<message name>, for example parent.message.

If the following code

(|
  firstParent* = traits something.
  secondParent* = traits different.
|

   copy.
)

contained copy slots in both parents, it would be necessary to choose specific target by rewriting a message sent to resend secondParent.copy.

Delegation is a fairly interesting concept that allows both single and multiple inheritance. In addition, it also allows things that classic languages can't offer, such as changing the parent slot at runtime to effectively switch to which object the undiscovered messages are delegated.

This, at first glance somewhat wild design feature, has quite interesting usage cases, for example parsers, where it allows you to easily change the context.

Comments

Comments are written in quotes:

"this is a comment"

Annotations

Annotations provide a way of adding metadata to objects, but there is surprisingly little documentation on how they work and can be used, even though they are used across the entire standard image.

The syntax uses curly braces:

(|
  p* = traits clonable.
  {'Category: accessing'
    slot = nil.
  }
|)

This example tells the graphical interface to make the slot appear in the accessing category:

Personally, I think that annotations come as a somewhat syntactically confusing feature, and I have managed to cause all sorts of errors when I created annotations with various random labels:

Still, annotations are used everywhere, especially for the Transporter, which uses them to indicate which object belongs to which module, when it was last updated, and so on.

Annotations are not visible in the object itself when viewed in the Outliner. You need to use a mirror (see the next chapter).

In terms of Stdlib's point of view

The trap of the simple languages, which syntax fits on a post card, lays in the complexity of the stdlib. Self is not an exception. I won't show you the whole stdlib, just the most important parts. Curious readers can see specific details in the Self itself.

Conditions

As is customary in the Smalltalk-ish languages, if conditions are implemented as messages sent to a bool object. Following messages are available:

ifTrue: []

ifFalse: []

and their equivalents with the "else" branch:

ifTrue: [] False: []

ifFalse: [] True: []

Loops

Like conditions, cycles are implemented as messages sent to either collections or to block objects.

Basic message is loop:

[ ... ] loop

This loops the body of a block unconditionally.

Additionally, conditional loops are available:

[ condition ] whileTrue: [ ... ]

and also equivalent messages whileFalse:, untilTrue:, ultilFalse:, loopExit and loopExitValue.

Other loops are do: messages sent to the numeric types, with alternative forms of to:Do: and to:By:Do:, which cycle to: given value By: some step, like for example range() function in Python.

There are also various transformers and iterators for collections, that work as loops. For example, you can send a message to mapBy:, mapBy:Into:, gather:, filterBy: and so on.

I can only recommend to look at the specific collection for the list of all available messages, because there are dozens of messages of this kind, allowing everything from filtering to searching, sorting, mapping, transforming, counting, and so on. There are definitely more messages than there are methods in Python.

Just for the sake of curiosity, here is a list of messages that a list collection can respond to:

<= x
> x
>= x
areKeysOrdered
copare: x IfLess: lb Equal: eb Greater: gb
copy
KeyedStoreStringIfFail: fb
max: x
min: x

Accessing

at: k
at: i IfAbsent: b
first
first: v
firstIfAbsent: noneBlk
isEmpty
last
firstLinkFor: elem IfPresent: presentBlock ifAbsent: absentBlock
firstLinkSatisfying: conditionBlock IfPresent: presentBlock ifAbsent: absentBlock
ifNone: noneBlock
ifNone: noneBlock IfOne: oneBlock IfMany: manyBlock
keys
last: v
soleElement

Adding

add: elem
add: v WithKey: k
addAll: c
add:allFirst: c
addFirst: elem
addLast: elem

Coercing

asList

Comparing

< c
= c
compare: c IfLess: lb Equal: eb Greater: gb
hash
isPrefixOf: c
isSuffixOf: c
equalsCollection: c

Concatenating

, c

Copying

copy
copyContaining: c
copyRemoveAll

Double dispatch from universalSetOrDictionary

unsafe_with: c1 Do: b FirstKey: firstK1 FirstValue: firstV1

Inserting

insert: x AfterElementSatisfying: blk IfAbsent: aBlk
insert: x BeforeElementSatisfying: blk IfAbsent: aBlk
insertAll: x AfterElementSatisfying: blk IfAbsent: aBlk
insertAll: x BeforeElementSatisfying: blk IfAbsent: aBlk

Iterating

do: b
doFirst: f Middle: m Last: lst IfEmpty: mt
reverseDo:
with: x Do: b
with: x ReverseDo: b
withNonindexable: c Do: b

InteratingWithEnds

do: elementBlk SeparatedBy: inBetweenBlk
doFirst: f Middle: m Last: lst
doFirst: f Middle: m Last: lst IfEmpty: e
doFirst: f MiddleLast: ml
doFirst: f MiddleLast: ml IfEmpty: e
doFirstLast: f Middle: ml
doFirstLastt: f Middle: ml IfEmpty: e
doFirstMiddle: fm Last: lst
doFirstMiddle: fm Last: lst IfEmpty: e

Printing

collectionName
comment1
printStringSize: smax Depth: dmax
statePrintString
storeStringForUnkeyedCollectorIfFail: fb
storeStringIfFail: fb
storeStringNeeds
unkeyedStoreStringIfFail: fb
buildStringWith: block
continued
defaultPrintSize
leftBracket
minContentsSize
minElSize
printStringKey: k
rightBracket
separator
statePrintStringOfElements
statePrintStringOfSize

Reducing

countHowMany: testBlock
dotProduct: aCollection
harmonicMean
max
mean
median
min
percentile: nth
product
reduceWith: b
reduceWith: b IfSingleton: sb
reduceWith: b IfSingleton: sb IfEmpty: mt
rootMeanSquare
standardDeviation
sum

Removing

remove: x
remove elem IfAbsent: block
removeAll
removeAll: aCollection
removeFirstIfAbsent: ab
removeLast
removeLastIfAbsent: ab

Searching

allSatisfy: b
anySatisfy: b
findFirst: eb IfPresent: fb
findFirst: eb IfPresent: fb IfAbsent: fail
includes: v
keyOf: elem
keyOf: elem IfAbsent: ab
noneSatisfy: b
occurrencesOf: v
occurrencesOfEachElement

setLikeOperations

includesAll: c
intersect: c
difference: c

Sizing

isEmpty
nonEmpty
size

Sorting

ascendingOrder
comment2
copySort
copySortBy: cmp
copySortBySelector: sel
isAlreadyKnownToBeSortedBy: cmp
sortedBy: cmp Do: b
sortedDo: b

Testing

isOrdered

Transforming

asByteVector
asDictionary
asList
asOrderedSet
asSequence
asSet
asString
asTreeBag
asTreeSet
asVMByteVector
asVector
copyFilteredBy: eb
copyMappedBy: eb
filterBy: filterBlock
filterBy: eb Into: c
gather: aBlock
gather: aBlock Into: aCollection
mapBy: eb
mapBy: eb Into: c

That's quite rich, isn't it?

Data structures

Data structures are made of layers of traits hierarchy.

All collections are based on key-value pairs. Even lists are based on a key value with individual elements being used both as keys and as values.

Self offers a rather rich variation of sets, dictionaries and trees:

Trees differ from dictionaries in the use of unbalanced binary trees, which can lead to degeneration and poor performance if you don't know what you are doing.

There is also a variation of lists, vectors, strings and queues:

The most important messages supported by virtually all collections include:

Important messages

Zpráva | Popis
at: | Get item at position / key.
at:Put: | To position / key put item.
add: | Add item (to the end if it is sorted collection).
addAll: | Add all items from this collection.
do: [ .. ] | For each item do this block.

Example

If you want to use the collection, just type the name in the shell/source code editor and clone it. This can be done with the clone or copy message (same thing for most of the objects).

WARNING: In prototype based systems it is really important to clone the collection. Other objects use the same prototype and if you don't clone the source, you'll change the prototype for every other piece of the code that works with the collection. This usually leads to a fast crash.

Now click on Get it.

Put the outliner somewhere on the desktop and "unpack" it using small arrow on the left:

You can see that the collection there contains zero elements (size 0). Open a shell in it and try to add something to it:

Now choose Do it. You can alternativelly choose also Get it and you'll get outliner for the result of the message call, instead of just evaluating the code.

As you can see, the value has changed. Now you can see what the object returned as a reaction to the values message is.

Here is the outliner for the result. You can put it somewhere on the desktop:

and unpack it again to see what is inside:

As you can see, the vector contains 'value' string at index 1. Keep in mind that the dictionary is unsorted.

Don't worry about the strange appearance of the Shell in the top left corner, I have broken graphics drivers and Self uses such prehistoric X bindings, that it lags and repaints it strangely. It works on my laptop as it should.

Here's an example of how to use to: to print elements and keys in the console:

do: (as all iterators in Self) expects a block, that can have two optional parameters, a value, and a key, named in the example as v andk. Note the somewhat strange order, one would logically expect the opposite (the key and then the value).

Collector

Collector is a special data structure that responds to the binary message &. Basically, it exists because Self does not have a literal for creating lists. To create a list, the easiest way is to use a collector:

(1 & 2 & 3) asList

Collector is neither a list nor a dictionary. It can be converted to all possible data structures by a message called as <Something>, for example asList.

Exceptions

Exceptions are not supported. Messages that might end up with an error usually offer an alternative in the form of a keyword message with the parameter IfFail:. You can see the example here with an object for accessing the operating system:

It is up to the programmer to use and handle the appropriate error (by passing the block with error handler code). If he does not, the debugger should appear, or the program will crash.

Likewise, if you want to allow error handling as a programmer of the library, you need to add a message variant with IfFail: parameter.

In my opinion, it doesn't seem to be the best solution, unfortunately that's how it works.

Object model

Note: A rather interesting discussion on the topic of Self's object model can be found here: https://news.ycombinator.com/item?id=14409088

As I explained, Self uses an object model based on prototypes. This can be summarized as saying that you copy new objects with a clone or copy message, or you create an empty object directly from the source code object literal with a reference to parent*, which provides a functionality similar to an inheritance.

As for the hierarchy of different objects, they are divided between traits and mixins.

Traits

These are "ancestors", that is, objects containing shared functionality, that are often fully functional by themselves. These objects are created in order to be shared by other objects via the parent* slots.

Self has a relatively rich trait hierarchy, as can be seen in the previous chapters.

Mixins

Mixins are small clusters of shared functionality, typically without a parent* slot, and shared only at some specific level of the object hierarchy. The purpose is to provide a functionality that is mixed into an object. Their equivalent is something like an interface with partial implementation.

Reflection with Mirrors

Mirrors are a specialty of Self, which I haven't seen in any other programming language.

Mainstream programming languages usually use reflection by employing various internal properties. For example, Python uses .__ class__, or.__ dict__, or .__ name__ to access internal object information.

Self uses mirrors. You can create a mirror by sending a reflect: message to an object with traits clonable in its parent hierarchy.

This will return an object that is mirroring the object given as a parameter.

You can see that it contains a slot pointing to the original object.

If you open the parent slot, you can also see all kinds of messages it can react to:

Note that I jump on the desktop to the left and right using the WIN + arrow keys shortcut. I just jumped halfway to the right.

In traits mirrors slots parent we can see that there isn't much, so we'll look at his parent:

You can see a rich list of categories that allow you to do all kind of stuff with a mirror:

You can for example list all messages that the object can react to:

Which is as you can see an empty set:

For clarification: the object, to which we created the mirror, contains no messages, only two slots. Here in this context, only slots, that contain code objects, are counted as "messages".

The slots themselves can be explored with messages from the slotAccess category:

You can see for example the result of the firstKey message:

The result was really the name of the slot "a". You can also resolve the whole slot:

The beauty of mirrors lies in the ability to disable them. To do that, you just have to remove the proper parent from the code where you want to disable the reflect: message, for example, by overwriting it with an object returning nil. This makes it possible to execute the code relatively safely by using functions like eval (of course you have to also remove the access to syscals and filesystem).

Tips & tricks

Over time, I collected useful notes, tips and tricks in my personal wiki, that can make programming in Self more pleasant.

copy message support

To support the copy message, an object must inherit some basic functionality, which you can find in the traits clonable.

Display parent slots in outliner

It's somewhat unfortunate to open up parents* in the outliner all the time just to see what's available for inherited slots.

Fortunately, the Outliner can be easily configured to display inherited slots by setting:

preferences outliner kevooidal: true

Open all subcategories

Sometimes it is very annoying to click on the black arrows when you have multiple nested categories. Double-clicking on the arrow opens all its subcategories.

Quit by sending a message

The environment can be terminated by the saveThenQuit or quitNoSave messages. Personally, I like to put a button on the desktop that calls one of these messages when pressed.

Build new image

If you don't want to use the default image provided with the Self distribution, and for some reason you want to build your own, you can do this from the objects/ directory in the project's source code repository (! it is required to do it from this directory!) by using following command:

Self -f worldBuilder.self -o morphic

-o does not specify the output file name (!) but the overclock. morphic parameter tells the worldBuilder that it should include the graphical interface.

When the script finishes, type into the console

desktop open

This will open the graphical interface and you can then save the image by using context menu.

Find slot

From any outliner:

This will give you an object, that can be used to look for a slot by its name:

Input bar at the bottom specifies the root of the search, the top one is used to specify what you want. Basic wildcards using * are supported.

Input bars are approved by clicking on the green square, or by pressing CTRL + enter. Then you have to click on the top arrow to start the search:

Individual slots can be opened by clicking the square next to them.

And you can also invoke all kinds of actions from the context menu

Read serialized objects from script

If you want to run one of the scripts or load your saved module, you can use the following approaches:

bootstrap read: 'name' From: 'directory'

Note that the name doesn't end with .self. Or:

'path/to/file.self' runScript

You can find more details here: Reading a module.

radarView

As I already mentioned in the previous episode, you can get the radarview by using following messages:

desktop worlds first addMorph: radarView

or

desktop w hands first addMorph: radarView

Better fonts

Maybe you did notice that Self's basic font looks really awful. It is originally looking for verdana, which is not installed on Linux by default, so it uses fallback. The solution is described here:

https://bluishcoder.co.nz/2015/01/08/improving-linux-font-support-in-self.html

Editor morph

Experimentally I found out, that if you want to use the editor, you need editorRowMorph containing editorMorph. This then responds to contentsString message.

I didn't find out how to use ui2_textField, ui2_textBuffer, textViewerMorph and uglyTextEditorMorph.

This episode is mostly about things that did not fit into other episodes (Environment and the programming language Self (part one; environment), Environment and the programming language Self (part two; language)). We'll have a more detailed look at the debugger, the transporter and some disadvantages of Self as a language, the environment but also of the principles of using prototype programming.

The user usage of transporter

I won't talk about how the transporter internally works, because you can read that in the paper Sifting Out the Gold: Delivering Compact Applications From an Exploratory Object-Oriented Environment. I will focus mostly on the user's point of view, as you can see it in the video here:

https://bluishcoder.co.nz/2015/11/19/exporting-self-objects-to-source-files-via-transporter.html.

This chapter contains many images. However, I would like to point out that there is nothing complicated going there;

I will make an anonymous object. I add it into new module using the transporter. Then, I create a slot in the global hierarchy, save the anonymous object into it and add the object to an already created module. Then, I tell the transporter using an annotation to not use the default value of that slot, but to follow the object, which it points to. When this is done, I'll save everything on the disk using the transporter dialogue.

I start with creating a very simple object, that contains one slot "a" with the value "1".

Then, I click on the outliner with the middle button (the scroll wheel) and choose "Set module...":

There are going to be series of dialogues, in which the transporter will ask me for different information. First, it asks about slots:

I want to add all of them.

Next it asks for the name of module, to which it should save the object.

I want "other", which will bring an input for a name. I want to name my module "test":

Dialogue is now asking me whether I really want to really create new module, or whether it was a mistake. I really want to create it:

I don't want this module to be a submodule of other modules, so I leave this field empty:

I confirm it again:

I choose the folder, to which the file should be saved. I leave the default "applications":

Now, my object is officially a part of the module, but it is still not stored under a path in globals hierarchy, which means that after loading it won't be easy to get a reference to it. I want to save it on the path globals test. So I open the outliner for globals by clicking the desktop with the scrolling wheel:

And I open the subsection "applications":

There are four applications in it. Clicking the "applications" label by the scrolling wheel, the context menu appears. I choose "Add Slot":

Then I delete the default text hint:

And I set the slot to nil. Notice that I am using <- for assignment (rewritable slot).

I save it by clicking the green rectangle or pressing Ctrl+Enter.

Now I graphically transfer the reference from nil to my anonymous object on the desktop. But I could as well just go into my anonymous object, open shell in it and write something like globals test: self.

The slot globals test now points to my object, that is, my object now exists on this path.

Now I only have to set the module for this slot by clicking on it with the scrolling wheel and choosing "Set module":

We can see a bigger menu, where I scroll completely down and choose other:

And I enter "test" again:

Because I don't have to scroll back up for a long time, I click on the empty desktop twice and choose to go "home":

Which sends me back to where I was before. Now need to adjust the annotation of the slot globals test, so I click on it with the scrolling wheel and choose "Show Annotation":

In the module setting I choose "Follow", instead of "Initialize to nil".

Now I click an empty place on the desktop with the scrolling wheel and choose "Changed Modules" from the menu:

The dialogue showing the changed modules shows up:

I can save them by clicking the buttons, so I click the button "W" right next to it:

And that is all. It may seem like many steps but the process is logical and dialogs guide you thru it. Now lets look at the "test.self" file that was created in the folder "objects/applications/" in the directory at the same level as the directory that we run the Self itself (../objects/applications/, don't ask me why):

''
 '
Copyright 1992-2016 AUTHORS.
See the legal/LICENSE file for license information and legal/AUTHORS for authors.
'
[
"prefileIn" self] value

 '-- Module body'

 bootstrap addSlotsTo: bootstrap stub -> 'globals' -> 'modules' -> () From: ( | {
         'ModuleInfo: Module: test InitialContents: FollowSlot'

         test = bootstrap define: bootstrap stub -> 'globals' -> 'modules' -> 'test' -> () ToBe: bootstrap addSlotsTo: (
             bootstrap remove: 'directory' From:
             bootstrap remove: 'fileInTimeString' From:
             bootstrap remove: 'myComment' From:
             bootstrap remove: 'postFileIn' From:
             bootstrap remove: 'revision' From:
             bootstrap remove: 'subpartNames' From:
             globals modules init copy ) From: bootstrap setObjectAnnotationOf: bootstrap stub -> 'globals' -> 'modules' -> 'test' -> () From: ( |
             {} = 'ModuleInfo: Creator: globals modules test.

CopyDowns:
globals modules init. copy
SlotsToOmit: directory fileInTimeString myComment postFileIn revision subpartNames.

\x7fIsComplete: '.
            | ) .
        } | )

 bootstrap addSlotsTo: bootstrap stub -> 'globals' -> 'modules' -> 'test' -> () From: ( | {
         'ModuleInfo: Module: test InitialContents: FollowSlot\x7fVisibility: public'

         directory <- 'applications'.
        } | )

 bootstrap addSlotsTo: bootstrap stub -> 'globals' -> 'modules' -> 'test' -> () From: ( | {
         'ModuleInfo: Module: test InitialContents: InitializeToExpression: (_CurrentTimeString)\x7fVisibility: public'

         fileInTimeString <- _CurrentTimeString.
        } | )

 bootstrap addSlotsTo: bootstrap stub -> 'globals' -> 'modules' -> 'test' -> () From: ( | {
         'ModuleInfo: Module: test InitialContents: FollowSlot'

         myComment <- ''.
        } | )

 bootstrap addSlotsTo: bootstrap stub -> 'globals' -> 'modules' -> 'test' -> () From: ( | {
         'ModuleInfo: Module: test InitialContents: FollowSlot'

         postFileIn = ( |
            | resend.postFileIn).
        } | )

 bootstrap addSlotsTo: bootstrap stub -> 'globals' -> 'modules' -> 'test' -> () From: ( | {
         'ModuleInfo: Module: test InitialContents: InitializeToExpression: (\'30.21.0\')\x7fVisibility: public'

         revision <- '30.21.0'.
        } | )

 bootstrap addSlotsTo: bootstrap stub -> 'globals' -> 'modules' -> 'test' -> () From: ( | {
         'ModuleInfo: Module: test InitialContents: FollowSlot\x7fVisibility: private'

         subpartNames <- ''.
        } | )

 bootstrap addSlotsTo: bootstrap stub -> 'globals' -> () From: ( | {
         'Category: applications\x7fModuleInfo: Module: test InitialContents: FollowSlot'

         test <- bootstrap setObjectAnnotationOf: bootstrap stub -> 'globals' -> 'test' -> () From: ( |
             {} = 'ModuleInfo: Creator: globals test.
'.
            | ) .
        } | )

 bootstrap addSlotsTo: bootstrap stub -> 'globals' -> 'test' -> () From: ( | {
         'ModuleInfo: Module: test InitialContents: FollowSlot'

         a = 1.
        } | )

 '-- Side effects'

 globals modules test postFileIn

The exported code is really ugly at first glance. I think it is because it uses only transported directives You can find all kinds of metadata set in it. This file can be versioned with git and it can also be loaded to the new image:

This gives us metadata about the module and in the outliner for globals applications we can see a new slot "test":

In which we can find our object:

The Debugger

Self has the debugger of the Smalltalk type, from which we can interactively change the code. Short demonstration will say more than a thousand words.

I create a simple object in Shell with slot "a", where the code, which should write the value of number "1" in the standard output, is saved. But I make a "mistake" and instead of the message printLine I write printLin without the e at the end:

This gives me an object, which I described.

I can check, if it is really the object by putting it on the desktop and unfolding the slots:

I open subshell in the object by clicking E in the right top corner and send the message "a" to the object:

Now I click on Get it, which should process the code (therefore write 1 into the console) and at the same time it should also give me the result of the operation into my hand (this should be object 1). But I have a mistake in the code, so instead I get a debugger:

The debugger looks a bit similar to the outliner. A box, that can be clicked on to unfold its various parts. Here you can see it unfolded:

We can see the whole stack trace and even more. We can also interact with it. For example, we see number 1 on the top left side, which is the message receiver (selector). We can click the number 1 and get the object:

In the case of number 1 it doesn't really make much sense, but with other objects, the live examination can be very useful.

We can also correct the mistake directly from the debugger:

The code gets adjusted after clicking on the green square. Notice the outliner next to it:

Now I click on continue and the whole interpret will continue, where it would usually continue, if there wasn't any mistake. So, the number 1 gets printed on the console and I get it to the hand as an output.

Debugger now indicates that it's dead and minimizes. Now if I click the button Get it again, everything will work as expected;

The Problems

Self is quite a sad heap from the practical point of view. I would like to say that it is usable to a big extent, but not enough for the beginners. If you need to solve something, you won't be able to google the solution. In most cases, you are probably the first one encountering such a problem. When not, there is quite a big chance that the problem hasn't been solved yet.

32 bit

Self is a 32 bit program. Porting it to 64 bit is possible but not trivial, so no one wants to do it. I don't think that anything will change with this in the next 10 years or so.

On one hand it is quite upsetting, because you can only make 4 gigabyte images, but this problem can be solved for example by using more instances, moreover you will probably find problems in something else much sooner. For example saving images is quite slow and the sizes of few tens of megabytes and gigabytes will take a few minutes.

Problems of the prototypes

Prototypes are easy to understand and together with the delegation (I am looking at you, JavaScript!) offer a strong and effective mechanism of code organization.

But they have one disadvantage; you forget to clone once and you will rewrite half of the system.

For example, you work with the prototype dictionary the way that you clone it and store values in it. But if you forget to clone it and you rewrite the dictionary directly, you will be writing to all new dictionaries that will ever be created by copying. This results in a quick crash of the system.

Sadly, Self doesn't offer any way how to prevent this. Personally in my language, I would like to avoid this problem by locking the system objects to read only mode, so the cloning would be forced before you could write into them. Or maybe by rewriting some setters to automatic clone of the used object. It would still be possible to change the underlying prototype object, but you would need to use mirrors. I am sure there are more possible solutions to this.

Namelessness of anonymous objects

Self treats its object literals similarly as languages supporting only lambda functions. Anonymous objects are great but it is generally easier to work with named things.

In Self, this is solved by annotations, where you can set the name of the object, which is then showed by outliners, or by placing the object into a hierarchy. If we place the object to the path globals dictionary, then it's obvious that the object is a dictionary.

It's worth to compare it to the languages based on classes, which usually have inverse problem when they try to name completely everything.

What is better and more natural for people? For the object to have its clearly given name or for the object's name to be given by a variable (or a path), in which it's stored?

No undo

Edit morphs do not support undo. Bummer. And all other keyboard shortcuts are some subset of default emacs keybindings.

Ctrl+C / Ctrl+V

Unfortunately it doesn't work because Self uses some really antiquated X bindings. The solution is to install the program autocutsel and run it in the background.

Missing documentation

There is no other documentation but the one I already mentioned in my articles. Usually you can ask about the problem in the mail conference, but that is not an ideal scenario.

Slots are unordered

This situation is quite sad in case when you want to use objects to directly store some information, because it makes you use ordered collections instead.

Self doesn't support cascading operator

If you don't know what it is, then you won't probably miss it. If you used Smalltalk before, it will hurt.

Unicode input / the output doesn't happen

Self uses ISO 8859-1 for text encoding, which is 8-bit encoding. That means that it doesn't accept any other characters then used in this character set (literally you just can't write them) and it doesn't even know how to show them.

For people like me, who live in non-english speaking country, this means that I can't type almost anything in my native tongue. My mother's surname is literally unwritable into the Self environment.

If you would like to look more into details:

https://github.com/russellallen/self/issues/113

Long outliners

Outliners are an interesting concept, which can quite quickly become annoying. You can have a look at an example:

http://kitakitsune.org/ltas/long_outliner_is_long/traits_xlib_display/

Or you can see the full problem here:

http://kitakitsune.org/ltas/long_outliner_is_long/traits_xlib_display_expanded/

Also, if you are looking into a more complex stuff, you end up with tens of opened outliners scattered all around the desktop.

Here is a module browser in Smalltalk:

Self and pictures

Self doesn't know how to show pictures in any of the usual formats. The only format it can show is some Sun's raster format:

https://bluishcoder.co.nz/2009/07/27/displaying-images-with-self.html

Previous episode Environment and the programming language Self (part one; environment), Environment and the programming language Self (part two; language), Environment and the programming language Self (part three; debugger, transporter and problems) were mostly about the language and the environment. In today's article, we'll have a look at the community around the Self language, its history, future and also possible consequences brought by Self.

The Community

The community around Self is somewhat untraditional. It is largely made up of academics and people who have been following Self for decades. They like to read the academic articles about or based on it, but they do not usually actively contribute to the language nor the discussion.

People

Self was primarily created by David Ungar and Randall Smith. Both of them join the discussions from time to time.

However, now the primary project leader is Russel Allen, who hosts the official GIT repository under his github account, maintains the official website and a blog, integrates merge requests and moderates the discussions.

Another highly engaged person is Chris Double, who often comments on github but also in the mail-list. You can also meet him on an IRC channel as doublec.

Mail list

Most of the communication is done through Yahoo groups mail list. If you don't want to log into a Yahoo account, then:

Post Message: self-interest[a]yahoogroups.com

Subscribe: self-interest-subscribe[a]yahoogroups.com

Unsubscribe: self-interest-unsubscribe[a]yahoogroups.com

List Owner: self-interest-owner[a]yahoogroups.com

NNTP

If you are interested in using the aforementioned mail-list as a usenet group, I highly recommend setting up the subscription via NNTP instead of an e-mail. You can use Gmane, where you can find the group as gmane.comp.lang.self. You can use, for example, Thunderbird to subscribe. Gmane website is not fully functional after a crash, but the NNTP interface is fine.

news://news.gmane.org:119/gmane.comp.lang.self

By the method of trial and error I got to understand that mail-list isn't really good for any kind of discussion, which isn't connected to Self, and people actively mind that you are discussing there because they suddenly receive emails from a group, from which they have received twenty emails in the last 10 years.

GitHub Issues

Personally, I recommend to move all technical discussions to GitHub Issues:

https://github.com/russellallen/self/issues

These are usually attended by people who are interested in the topic and will not hate on you when you think out loud about code and possible solutions.

IRC channel

In addition, there is a semi-dead IRC channel where somebody sometimes comes and causes a discussion:

irc://#self-lang@irc.freenode.org

Personally, I have been idling there for over a year, and I have to say that discussions on all possible topics are welcome, it's just that most people don't actively watch that channel. Therefore it can take even days before they start to react. Certainly do not get annoyed by it and leave the client running in the background (irssi on a server) for a longer time, if you really want someone to answer.

Social networks

There is a subreddit /r/selflanguage, where the news notifications are being sent. Most of them are then resent to hackernews, /r/programming and on lobsters.

Blog

There is also a blog, but with a text width of 460 pixels, it looks really bad. Also, you are not free to write there whatever you wish and if the owner doesn't like it, then he will delete part of your text (already tried that, ehm ehm). Therefore, it's not a general blogging environment, unlike other blogging services.

Personally, what bothers me most about it, is that with its format it is absolutely not suitable for writing technical texts, and I don't only mean the editing in Wordpress, mostly it's about the displaying in a horrible noodle in the center of your screen (really absurd on 4k LCDs), which brings the worst from the mobile to your computer screens.

If you are interested in contributing there, you can send an e-mail to Russel Allen and he will make you an account within a few days. However, I would recommend you to make your own blog.

You can also subscribe to receive the blog news through NNTP:

news://news.gmane.org:119/gwene.org.selflanguage.blog

and RSS:

https://blog.selflanguage.org/feed/

Historical point of view

The first version of Self was designed by David Ungar and Randall B. Smith in 1986 in Xerox PARC, which was also the birthplace for Smalltalk, Ethernet, laser printers and much of today's graphical user interface concept.

The development then continued from 1987 at the Stanford University by the team comprising of Craig Chambers, Urs Hölzle, Ole Agesen, Elgin Lee, Bay-Wei Chang and David Ungar. The first versions of the compiler and of the graphical interface based on the Morphic framework had been created at that time.

In 1991, the development had been transferred to Sun Microsystems, where few versions had been published together with the prominent 4.0 version in 1995. At that time, the first work on the first JIT (Just in Time) compilers was in progress. Thanks to them, Self had been optimized to the extent that it was capable of running numerical benchmarks around the speed of half of the performance of optimized C code. Also, a Smalltalk interpreter had been implemented at that time in Self that was working faster than the native compiled versions.

Then Sun decided not to support Self anymore and decided to support Java. As Chuck McManis, a former employee of Sun Microsystems, wrote for a thread on hackernews:

Wow. For those who don't know, there was a big 'language bake off' at Sun between TCL, Java, and Self (all being funded by Sun Labs) and Bert Sutherland (then director of Sun Labs and brother of Ivan) required that the language changes stop and then we'd look at each one, and decide which one to move forward on. When the world sort of exploded at the WWW conference held in Darmstadt Germany in 1995, Java officially 'won' and both Self and TCL were de-committed. (not canceled per-se but not getting any more funding either). I like to think that all three languages benefited from the competition.

Asked why did Java win, he replied:

At the time it was the ability to construct executable content inside a web page. Javascript now dominates that space, but up until WWWC 2 pretty much everyone was stuck with page layout primitives. It made it possible to see a path to where we are today and so everyone wanted their browser to have it, and if their browser didn't have it they could run the HotJava browser and get it. Bottom line, it demonstrated an answer to a problem that a lot of people were having, and it promised to answer that problem in an 'open source' kind of way.

That was the end for Self for many years. It's not that it would completely disappear, but in combination with hardcoded optimizations that made it run only under SPARC processors, many people stopped caring about it.

In 1997, Sun bought Animorphic Technologies, which constituted of people, who developed the JIT optimization in Self. They created a Hotspot technology, based on their development knowledge from Self. This accelerated (then) very slow and low-performing Java to usable speeds. This way Self lost important people and one of the biggest disadvantages of Java, its slowness, disappeared.

First attempts to run Self on Linux began in 1999, but from what I understand, it was very uncomfortable and aimed at hardcore programmers. At that time, thanks to a port by David Ungar, it was possible for the first time to run Self on Apple hardware that used PowerPC processors. Macbooks were cheaper and more available than a Sparc workstation from Sun, so now, for the first time, Self could be used even by "mere mortals".

David Ungar receivedDahl-Nygaard price for Self in 2009.

A year later in 2010, Russel Allen published version 4.4. It was the first version that was officially natively working on Linux. It has to be said that it was possible to run Self within the project Self4Linux since 2001. However, it still wasn't an official version. Before that, the port by Gordon Cichon from the year 1999 could also be used.

Since the publishing of 4.4, the development is still led by just a few people. The latest version at the time of writing this article is 2017.1 published in May 2017.

The Future

Progress

Nowadays, slow and sporadic works are being done on Self. They mostly consist of repairs and adjustments. I've begun to add support of Unicode to the graphic interface. Although I managed to do certain progress, I was unable to continue due to personal reasons, which made me prioritize projects and really ate my free time (I have a kid now, a different job, you know how it works..). So probably no UTF support in the foreseeable future.

The graphical interface definitely needs a lot of work. It works, but by today's standards and expectations it is really uncomfortable and merciless for users in terms of error toleration. I managed to crash the whole system several times during my first half-hour just by trying different stuff. You don't need to make any big mistake, it's enough to just make a typo in your GUI code and the system will at least freeze the GUI, if not crash entirely.

A mechanism, which is known from some Smalltalks (Pharo for example), that would enable restoration from protocol to the point before crash, or even automatic saving of some of the restoration points, was discussed many times in the email conference, but as far as I know, no real progress was made towards this feature. Furthermore, it would be nice if it wasn't so easy to crash or freeze the system.

All internal text editors need to be improved because often they don't reach even the functionality of the Notepad in Windows. Working with copy/paste buffer needs to be updated because Self works with it only in some weird 30 years old mode and you need to have autocutsel running in the background to make it work.

Personally, I think that there is a big space for improvement of the interface and the language in the form of internet project repository, from which it would be possible to install through dependency definitions. In other words; it is quite hard to distribute your own software in Self nowadays. A package manager would greatly help this situation.

Klein

There used to be a project that aimed to bootstrap Self in itself called Klein.

What's left of it can be found on Github:

https://github.com/AdamSpitz/klein

If you want to continue Self development, this could be a good starting point.

I find it interesting, that you could debug Klein and connect to it from the running instance of standard Self.

Korz

Korz is a very interesting project; David Ungar had an idea about how Self would look and work if he added another dimension.

Languages that support only functions, such as C or Pascal, have only one dimension. A function receives some data.

Object-oriented languages added another dimension by interlinking functions with structures they work with. A function now receives some data and can be found in different objects which give them different meanings.

Korz adds another dimension in the form of whole worlds. Objects are stored in one or more worlds. Compared to Self, not only an object and a message are specified, but also the world, to which it should be sent. Worlds can be different, in some, for example, mirrors and functions for working with a system and a disk are missing entirely, so they work like a sandbox. They can dynamically emerge and cease to exist, objects can be only in one world or in more worlds at the same time.

All of this could be simulated by using the objects, that would store other objects, but we would need to do many things manually. Korz offers this as a built-in and it's supported right at the level of a virtual machine.

You can find a better description in the papers:

Korz: Simple, Symmetric, Subjective, Context-Oriented Programming

Subjective, multidimensional modularity with korz

Protip: If you don't know how to get the papers, use scihub (Google will help you).

Unfortunately, that is all that I can say about Korz. As far as I know, it is not an open source software and its author didn't talk about it too much, and he also didn't react to questions in the conference:

https://groups.yahoo.com/neo/groups/self-interest/conversations/topics/3631

The Metaphysics

"Metaphysics" might be an exaggeration but I wanted to express the context, possible potential and vision of Self, which I partially picked up from the historical materials, and partially dreamed up.

Tarraingim

Few years back while reading through complete archives of Self conference I found something, that still keeps on lighting up my imagination. It's a proof of concept of a system for working with information and visualization of programs called Tarraingim, Tgim in short. It was created by James "kjx" Noble as his academic work.

https://web.archive.org/web/20010813120828/http://www.mri.mq.edu.au/~kjx/tgim.html

https://web.archive.org/web/20010423002056/http://www.mri.mq.edu.au:80/~kjx/finder.html

https://web.archive.org/web/20010903122635/http://www.mri.mq.edu.au:80/~kjx/stack.html

https://web.archive.org/web/20010309054734/http://www.mri.mq.edu.au:80/~kjx/quicksort.html

https://web.archive.org/web/20010824102108/http://www.mri.mq.edu.au:80/~kjx/parse.html

The system made it possible to display and browse objects the way we are used to browsing files, using icons and various visualizations.

Of course, the images above do have their limitations and the original intention was more in the direction of working with programming languages. Yet, I cannot stop wondering and imagining how visualizations like this could be used for various information representations and personal wikis.

As Alan Kay said:

Point of view is worth 80 IQ points

I think that Tarraingim was a fascinating experiment on the topic of the environment, that gives you many prepared points of view.

Engelbart's MOAD

It has been 50 years since, probably, the most influencing computer presentation of all time: so-called Mother of all demos by Douglas Engelbart.

More information here:

The 50th Anniversary of Doug Engelbart's Great Demo

If you missed this demo, I can only recommend watching it:

For many people, Engelbart's demo is a visionary thing. They often praise it as a precursor of modern computer technology, which predicted many modern technologies, such as graphic user interface, the Internet, interactive video calls and other conveniences of our network age. It was the first time a computer mouse was used!

At first glance, it should be a phenomenal success. After all, who can predict and define the future as well as Engelbart did?

But for Engelbart it was quite a disappointment. A disappointment, which can still be felt around lisp and Smalltalk communities. You can hear about it from Alan Kay, or Bret Victor;

What is Still Missing

Education Presentation by Alan Kay @ SRI/Engelbart

A few words on Doug Engelbart

The Father Of Mobile Computing Is Not Impressed

In short only a less important part of his vision got fulfilled. The original vision was about acceleration of human progress through attempts at empowering the human intelligence.

This should have been achieved by using computers as assistants, as Vannevar Bush defined it in his famous paper As We May Think. He wanted to use the strategy of boostraping; process improvements for process improvements. You can find the specific details in his work Augmenting Human Intellect: A Conceptual Framework.

He never intended to create a user interface for masses, so people could intuitively use computers and not know or understand anything about them, as it was popularized by Apple. On the contrary! His lifelong effort was concentrated on interconnecting people and computers in the tightest possible manner.

If you allow me to use a rough analogy, then what he was trying to achieve was similar to mathematics and education in mathematics. A system, which he was creating, was supposed to be like math for a person, who is trying to build a bridge. His "conceptual framework" was supposed to give people a way of thinking and an ability to use a computer as an extension of their minds. That is in sharp contrast to how we use computers today, which is mostly a medium that allows us to record, edit, search and broadcast our digital creations.

His idea was that a person trying to do intellectual work would be able to program the computer and instruct it as effectively as possible by creating a closed loop with it so that he could no longer tell where the person ends and the machine begins.

What I mean by this is possible to understand if you have ever used a program so much, that you no longer perceived its controls and had a feeling, that you are communicating with a computer only using your thoughts. This kind of symbiosis can be seen when you look at the programmers, who often customize their editors and use them on a day to day basis so that they are highly effective and can use all kinds of text transformations that are unavailable to untrained people.

Or a person who is modelling a 3D object in a graphical editor and after a while no longer thinks about the controls of the program, only about the modelling. Players also often forget that they are pressing wasd and instead feel like they are inside the game.

Engelbart wanted to create a system for working with computers and information in general, in this manner. That's why he created his NLS (oN-Line System), and that is why his demo is still so amazing even today. Most of the programs of today fall behind because they are limited by paradigms of graphic interface and the effort to make them as intuitive as possible. If some program actually achieves usability envisioned by Engelbart, then it's usually a specialized matter. You can't use CAD, with which you design houses, to write a document or create your own personal information database.

If you think about it, work with information was, apart from its presentation, reduced only to office packages and writing notes in wiki. You can't, for example, download all your ratings from IMDB, correlate them with your friends and recommend them films they may like, without being a programmer.

Self

You may be wondering how Self is related to all of this. Personally, I hope that it is clearly visible from the already published articles that I consider Self to be the closest thing to the Engelbart's vision from everything that I am aware of.

Just think about it:

An object-oriented graphical interface that can be assembled and disassembled in a relatively intuitive way to quickly construct user programs.

Objects as an underlying structure for working with information.

Interconnection of the programming language with the entire environment, so there is no clear separation between where one ends and the other begins.

The resulting addressability of all items simply through messages to the objects.

Of course, Self is in a very desolate state. Some of the ideas were just outlined, others were put into practice sometime in 1992 and no one has touched them since. Just have a look at this short preview of a demo snapshot:

It's hard to not compare it to the possibilities of Engelbart's NLS.

I don't want to claim that Self is what Engelbart envisioned. In my opinion, however, many of his thoughts went the same way*. If it had been finished properly, then we would have an infinitely programmable environment where everything is an addressable and callable object. A graphical interface environment with a paradigm that allows everyone to create their own graphical applications. A system that, with a little effort, can be used as an extension of one's mind.

I think it's worth thinking about.

* See my question in the conference about the inspiration for Self: https://groups.yahoo.com/neo/groups/self-interest/conversations/topics/3627

Concluding remarks

That's all I can say about Self. A small part of a facet of a polyhedral crystal, reflection of sparks of ideas, words about the thought model in motion.

Even if it didn't change my point of view on the world of programming languages, interactive environments, and graphical programmable systems, it still completely changed my thinking about reflection. And that is something, which shows through everything that I have been lately thinking about and what I have been working on.

Things created in a computer can have any shape. And somehow, as humanity, we have made a collective decision, probably totally unconsciously and certainly wrongly, that they won't be as dynamic as they could. Self showed that there is a possibility for everything to work differently.

I'm a witness to Self still hiding a lot of magic, which hasn't been seen by many. Secrets visualized by the mind. It's up to you to reach out and look at them.