Перейти к основному содержимому

5.08. Особенности синтаксиса

Разработчику Архитектору

Особенности синтаксиса

Синтаксис Smalltalk — один из самых минималистичных в истории.

Здесь всего 6 ключевых слов: true, false, nil, self, super, thisContext. Нет специальных конструкций для циклов — есть методы вроде timesRepeat: или do:.

10 timesRepeat: [ Transcript show: 'Hello' ].

Это противоположно языкам вроде C# или Java, где синтаксис насыщен ключевыми словами и конструкциями. В Smalltalk поведение не вшито в язык — оно делегировано объектам. Поэтому вам и учить особо ничего не нужно.

true это единственный экземпляр класса True. Он представляет логическое «истина», как и везде. Пример:

(x > 0) ifTrue: [ ... ]

false - единственный экземпляр класса False. Логическое «ложь». Пример:

(x < 0) ifFalse: [ ... ]

nil - единственный экземпляр UndefinedObject, обозначает отсутствие значения. Пример:

var := nil

self - это ссылка на текущий объект (аналог this в Java/C#). Пример:

^ self name

super - вызов метода у родительского класса. Помните «суперклассы»? Как раз оно. Пример:

super initialize

thisContext - ссылка на текущий контекст выполнения (стек-фрейм).

Что важно - это не операторы и не управляющие конструкции, не типы данных, это просто объекты или ссылки, встроенные в систему.

В большинстве языков проверка на то, что x>0, к примеру, в Java, будет такая:

if (x > 0) {
System.out.println("Positive");
}

В Smalltalk:

(x > 0) ifTrue: [ Transcript show: 'Positive' ]

Как можно обратить внимание, нет ключевого слова if. Так же нет for, while, class, function, return — всё это реализуется через сообщения и объекты. В данном случае, x>0 - это выражение, которое возвращает объект true или false. Мы посылаем этому объекту сообщение ifTrue. true выполняет блок, а false игнорирует. То есть, условие не является синтаксической конструкцией, а представляет поведение объекта. То же самое с циклами:

10 timesRepeat: [ Transcript show: 'Hello' ]

Звучит как английское выражение да? Повтори 10 раз - тут 10 это объект, timesRepeat - сообщение, а [] - блок кода, тоже объект. И нет циклов вроде for.

В Smalltalk каждое выражение возвращает объект. Даже присваивание:

x := 5    "возвращает 5"

Даже блок:

[ 2 + 3 ]    "возвращает сам блок (ещё не выполнен)"

Выполнение блока:

[ 2 + 3 ] value    "возвращает 5"

Это позволяет строить цепочки выражений:

(3 + 4) factorial negated abs    "7! = 5040, negated = -5040, abs = 5040"

Каждое сообщение возвращает объект, на который можно послать следующее сообщение. А блоки - это не анонимные функции, а полноценные объекты.

Синтаксис у блоков таков:

[ :параметры | тело ]

Примеры:

[ Transcript show: 'Hello' ]           "без параметров"
[ :x | x * x ] "с одним параметром"
[ :a :b | a + b ] "с двумя"

Блоки можно хранить в переменных:

square := [ :x | x * x ].

Передавать как аргументы:

numbers do: [ :n | Transcript show: n printString ].

Выполнять:

square value: 5    "→ 25"

Это предшественник лямбд в Java, замыканий в Python, стрелочных функций в JS.

А как посылаются сообщения?

Весь синтаксис Smalltalk строится вокруг трёх форм посылки сообщений.

  1. Унарные сообщения. У них нет параметров, имя - одно слово, и они имеют высокий приоритет. Пример:
42 factorial
'hello' size
Date today

Здесь объект 42 отправляет себе сообщение factorial.

  1. Бинарные сообщения. Это что-то вроде операторов из символов +, -, *, /, >, <, =, , и др, но это просто сообщения.
3 + 4
'hello' , ' world' "конкатенация строк"
x > 0

3+4 - объект 3 получает сообщение 4 с аргументом 4.

Можно определять свои:

Number >> @@ 
^ self abs

И теперь (-5) @@ будет 5.

  1. Ключевые сообщения. Имя сообщения разделено на части, каждая из которых заканчивается на : (двоеточие), а каждая часть принимает один аргумент:
'Hello' indexOf: $e startingAt: 3

Здесь сообщение это indexOf:startingAt: а аргументы - $e и 3.

Другие примеры:

String with: $A           "создать строку из символа"
Dictionary at: 'key' put: 'value'
Point x: 10 y: 20

Сначала выполняются унарные, потом бинарные, потом ключевые. Внутри группы слева направо, а скобки меныяют порядок:

3 + 4 * 5    "→ (3 + 4) * 5 = 35"
3 + (4 * 5) "→ 23"

Знаки препинания тоже имеют свои значения:

. (точка) это разделитель выражений. Как точка с запятой в C/Java. К примеру:

x := 5. y := 10

: (двоеточие), как мы выявили ранее, указывает на параметр в ключевом сообщении.

` (апостроф) разделяет объявление переменных в блоке или методе.

[] (квадратные скобки) определяют блок кода.

() (круглые скобки) группируют выражения.

:= (двоеточие и равно) определяют присваивание:

count := 0

" (двойные кавычки) определяют комментарий.

Как можно заметить, нет фигурных скобок, нет ; в конце строк, нет return.