Unknown Smalltalk

  • Tutorial


Dear readers of Habr. First of all, I want to explain what Smalltalk does in the corporate blog FLProg. The fact is that both the FLProg program and the program website are written in this wonderful language. Its capabilities and the tremendous speed of development on it allow me to maintain and constantly increase the functionality of both the site and the program. If you are interested in how I manage to do this, I ask for a cat.

First, a little background. Nine years ago, I worked in an industrial company as a circuit engineer. He drew diagrams, and in his spare time he wrote small programs on dolphins to facilitate the harsh engineering life. The programs gradually began to diverge across the team, the management noticed this and an application programming department was created. To help me, I was hired by a professional programmer who recently completed a project. So he introduced me to the Smalltalk language in which his previous project was implemented. Honestly, my first reaction from this language was negative. He broke all my ideas about programming. Breaking lasted about three months, after which I fell in love with Smalltalk and still prefer to work only on it, although during this time I had to study a few others.
Since the narrator is not very much of me, I will widely use materials from the Russian Wikipedia devoted to this language and arrange these materials as citations.
History of the language:

Smalltalk was created by a team of researchers led by Alan Kay at the XEROX Palo Alto Research Center. The first implementation, known as Smalltalk-71, was created in a few months as a result of a debate that a programming language based on the idea of ​​sending messages prompted by Simula should be implemented on the “code page”. The later version, actually used for research, is now known as Smalltalk-72. Its syntax and execution model was very different from modern Smalltalk, so much so that it should be considered as another language.
After significant revisions that capture several aspects of the execution semantics to increase efficiency, a version known as Smalltalk-76 was created. In this version, inheritance was added, the syntax is closer to Smalltalk-80, and the development environment includes most of the tools now familiar to Smalltalk-erams.
The first years of the language’s life are described in Kay’s remarkable essay, The Early History of Smalltalk ( HTML ).
Metaclasses were added to Smalltalk-80, making the phrase “all objects” true by associating individual classes of properties and behavior (for example, supporting various ways to instantiate). Smalltalk-80 was the first version available outside of PARC, first as Smalltalk-80 Version 1, distributed to a small number of companies and universities for peer review. Later (in 1983), a public implementation known as Smalltalk-80 Version 2 became available as an image (a platform-independent file containing objects) and virtual machine specifications.
There are currently two Smalltalk implementations that are direct descendants of Smalltalk-80. These are Squeak and VisualWorks. What Smalltalk-80 looked like can be seen in the screenshot. The Smalltalk-80 version 2 image is running on Hobbes, the ST-80 virtual machine implemented on VisualWorks.
Smalltalk has had a great influence on the development of many other languages, such as: Objective-C, Actor, Java and Ruby. Many ideas of the 1980s and 1990s on writing programs (Class-Responsibility-Collaboration card) appeared in the Smalltalk community, such as design patterns (for software), extreme programming and refactoring. WikiWiki concept founder, Ward Cunningham, is part of the Smalltalk community.


To date, the latest version of the language is 8.0, released in September 2014. Specifically, I work on the implementation of VisualWorks and the story will be devoted to it.

Smalltalk is an object language, so it will be appropriate to recall the basic principles of OOP (in the form in which they were formulated by Alan Kay):
Object - the basic unit of an object-oriented system.
Objects may have state.
Sending a message is the only way to exchange information between objects.
In addition, the Smalltalk object model is built on classes, which means:
Each object belongs to a class.
Functionality of an object is determined by its class (a set of its methods).
Classes are organized in a hierarchy.
Classes inherit functionality from their ancestor (or ancestors).
That, in fact, is all. Although you can further emphasize some principles and clarify others:
Everything in Smalltalk is an object. Those. that's all. Everything. There is nothing that is not an object.
There are four types of actions in Smalltalk - sending a message, assigning, returning a value from a method, and invoking a virtual machine primitive.

A little from myself. All variables in objects are pointers. There is no concept of dereferencing a pointer; variables always refer to an object. Secondly, Smalltalk is a dynamically typed language. That is, in any variable you can put a link to an instance of an object of any class. Accordingly, there is no concept of type conversion. Thirdly, the language has a very advanced garbage collector, so you almost never have to use methods to destroy an object. In the case when there is not a single link to the instance of the object, it is quickly destroyed by the collector and the memory is freed. Of course, this is a very simplified description of the garbage collector, but it is very difficult to achieve memory leaks in Smalltalk (I have never succeeded).

Smalltalk architecture

Any Smalltalk system (development environment, separate executable program, etc.) consists of two parts - a virtual machine and a system image.

Virtual machine

A virtual machine contains basic functionality: a bytecode processor (in the form of a JIT compiler or interpreter), a memory management system (with a garbage collector), and primitives. (Primitives - a functionality that for some reason turned out to be more convenient and profitable to implement than the Smalltalk, and the virtual machine is usually the lowest level functionality, such as adding numbers or draw a point on the screen.).

The system image

A system image (image) is a file that stores all the objects of the Smalltalk system between work sessions. These objects include executable programs, created objects, classes, etc.
When the Smalltalk system is running, all actions, of course, are performed in the computer's memory. When you exit the environment, you have the choice to either save the current state of the system image or not. If you save the system image, then at the next start the whole environment will be restored very accurately - up to the position of the windows and cursor positions. This is because the states of all objects are saved (and then restored).

The system image is the main container of information in Smalltalk - i.e. Smalltalk does not accept text files with source code. In principle, they can be created, but working with the system image is much more convenient.


Currently, there are virtual machines under Windows x86 (works fine under Windows x64), Windows x64, Linux 32, Linux 64, Solaris, Mac Oc. To switch to another platform, it is enough to attach the necessary virtual machine to the system image. In the code, sometimes it is necessary to provide different behaviors for specific API calls, but most of these issues have already been resolved in the base classes. For example, working with the file system does not require any changes at all when switching to any platform. I had a need to refine the code when porting to Linux only in the area of ​​working with Com port, and that took no more than a couple of hours.

It was all a theory, and now to practice.
The big advantage of Smalltalk is that there is no need to install the environment on a machine. It is quite possible to create a folder on a USB flash drive, put three files there and the workplace is always with you. You can work on any machine, even on someone else’s, even without administrator rights, and after removing the flash drive, there is no trace of work left.

The main Smalltalk working environment:

Class Browser


Workspace


Class Browser stores all program code representing a set of project classes and base language classes. The main work takes place in it. It should be borne in mind that all classes are “living”, that is, at any time you can call any class, create its instance, and make the necessary changes. For all this, Workspace serves. In his working field, you can write and execute any code.

There are many ways of programming, but for myself, I chose the method of “Programming through debug”. I didn’t come up with it, but I spied on the rally of programmers on Smalltalk, which was held in St. Petersburg eight years ago. Unfortunately, I do not remember the speaker. As far as I know other languages, in no other way this method will be possible. The fact is that Smalltalk allows you to stop the program at any time, make corrections to the code and continue execution from the breakpoint. It’s difficult to explain, it’s easier to show.


To store the history of the project, it is desirable to have a database for the version control system. At Smalltalk, it is implemented on the basis of a database, and can work on almost all known database engines. I have PostgreSQL installed on my server, and I can connect to it from anywhere with the Internet. The version storage system supports storing code histories from the very beginning, creating code branches, merging different branches, the ability to roll back to any version. In addition, it is possible to view the history of changes of any method in the system, and download the necessary version of a particular method.
But the presence of such a base is not mandatory. In addition to the code storage system in the repository, there is also a ChangeList. It automatically saves all changes to the code from the moment you start working in the image. With it, you can restore all changes, for example, in the event of a system crash (this sometimes happens with too rude experiments), turning off the computer, for example, when turning off the power. In this case, it is possible to restore only the necessary methods, for example, excluding erroneous ones. As with the version storage system, you can see all the changes to a specific method, and restore any version if necessary.

And finally, the most delicious Smalltalk features are DEBAG and REFACTORING. And I didn’t just write them in capital letters.
Let's start in order.
The debugger built into Smalltalk, when an error occurs in the code, stops the program from executing to the error point, allows you to view the stack of methods with the contents of all variables, make a change in any of the previous methods, change the contents of the variables, and start the program from any of them further. But it is also easier to show than to explain.


As I said in the video, this is only a small part of the possibilities for solving errors implemented in the language. A full description of these features, perhaps pull on a small booklet.
Refactoring Key features:
Change the name of any class, method with automatic tracking of all calls and references. In this case, it is possible to select mutable methods.
Changing the name of a variable, with automatic tracking of the application of this variable within the class. Here it is necessary to clarify. All variables in Smalltalk are private. That is, they are available only within the class in which they are created. External access only through accessors (getters and setters). Accordingly, methods that can use a variable are present only within the same class.
Adding or removing method parameters with automatic change of all calls to this method.
The ability to view all method calls, as well as view all class references.
Well, there are many other functions that deserve separate consideration. I also want to show this in my work.


Here are a few features of the language that tear programming patterns in the early stages of getting to know the language.
Priorities of mathematical operations. They are simply not there. Since numbers are objects like everything else, operations are ordinary methods. And performed in the usual sequence. For instance.
a: = 1 + 2 * 3/4.
How will this be done in a regular language? As far as I remember (if it’s wrong - correct) 2 times 3, then the result is divided by 4 and added 1.
On the smoltolka, 2 is added to 1, the result is multiplied by 3 and divided by 4. You just need to remember and remember to use brackets. In order to perform actions in accordance with the result in a regular language, it is necessary to write this:
a: = 1+ (2 * 3/4).

Collections.Collections are analogous to arrays in other languages. There are many different ones (Set, OrderedCollection, ByteArray, etc.). By the way, a string is the same collection, and most of the methods related to the collection apply to it. The first feature in the collection may be objects of completely different classes. The second, most unusual for any programmer, feature of the collection is that the indices begin with 1 and not with 0. Accordingly, the first character in the line has index 1. And in any collection, the same. You have to get used to this.
Well, the third is the main one. There are no primitives. All are objects. Even Class is an instance of the Metaclass class.
Metaclass is an instance of the Metaclass class. Here is a ring at the very top.

Well, a little more about the goodies.
Calculations without loss of accuracy.The main problem in many languages ​​is the loss of precision in division. For example: 1/3 is 0.3333 and 3 in the period. Accordingly, in other languages, the number of decimal places is specified, and accuracy is lost. Smalltalk solved this problem very nicely. As a result of this division, we get a Fraction class number. It contains the numerator (1) and the denominator (3). That is, as such, there will be no division, but there will be a fraction. We are not losing anything. And all subsequent operations obey the rules for working with fractions. When it is necessary to get a decimal number, we just tell this object asFixedPoint: with the desired number of decimal places, and we get the result. Moreover, the number itself remains unchanged, and we can continue to carry out calculations without loss of accuracy. In other languages, I have not seen this.

Serialization of the object structure.I know two packages: BOSS and SIXX.

BOSS - saves any object structure to a file while preserving the uniqueness of objects. Saves very quickly to a binary file. Keeps very compact. There are downsides. With any change in the declaration of saved objects (adding, deleting variables, changing the name of variables, etc.), the file is not deducted back. For my tasks did not fit.

SIXX - saves any object structure to a file while preserving the uniqueness of objects. Saves slower than BOSS, and the file is bigger. The text file is somewhat similar to XML. Great advantage. In saved classes, you can safely delete, add variables, it is undesirable only to change their names. If a variable is added, then when subtracting from the file, it is initialized by nil. This behavior allowed me to open projects saved in the first versions in the program, although a lot has changed in the project since then.

Work with databases.I use the GLORP package included in the basic package. The package serves to save the database and restore objects of any complexity to the database. For the objects that are saved to the database, it is necessary to describe once the way to save them, and then all the work with the database will be as follows:

1. Get the database
session: session: = FLProgRuDescriptorSystem sessionForLogin: FLProgRuDescriptorSystem login.

2.Save or update the object in the
session save: object database .

2. Subtract all objects from the database of a particular
collection: = session readManyOff: Foo class .

3. Subtract with the condition:
all where the value of the variable is id = 100
collection: = session readManyOff: Foo where: [: each | each id = 100].

the first is where the value of the variable is id = 100 and the value isCorrect = true
object: = session readOneOff: Foo where: [: each | (each id = 100) & (each isCorrect = true)].
4. Removing an object from the
session delete: object database

I haven’t yet touched upon the issues of creating a GUI for the application, testing issues (the SUnit package is built-in, probably the most powerful testing tool from the languages ​​I know), application deployment and WEB capabilities. But the post turned out to be huge, and if we consider them as well, then no one will finish it to the end. If you like the post, then I will try to choose the time and talk about the rest in the sequel.

If you are interested in this language, then you can read more about it here:
Russian Wikipedia dedicated to the language of the resources of which I used

Smalltalk Russian-speaking user group

I will try to answer all your questions for comments

Also popular now: