Bystroushaak's blog / English section / Series about Self / Environment and the programming language Self (part three; debugger, transporter and problems)

Environment and the programming language Self (part three; debugger, transporter and problems)


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:

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:

Long outliners

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

Or you can see the full problem here:

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:

Last episode

Last episode will be about Self's community, history, possible future and there will also be some philosophical thoughts.

Relevant discussions

Become a Patron