Синтаксис и особенности языка
С чего начать чтение
В Smalltalk нет if и for — есть сообщения объектам. Практика: Playground в Pharo (первая программа).
Сначала: Циклы в коде — общая идея повторений, виды циклов и типичные ошибки без привязки к синтаксису языка.
Особенности синтаксиса
Минимальный синтаксис языка
В ядре Smalltalk мало встроенных конструкций. Ветвления, циклы и условия — это объекты и их методы (ifTrue:, timesRepeat:, whileTrue: и т.д.); отдельных операторов if / for в синтаксисе нет.
| Элемент | Роль |
|---|---|
| посылка сообщения | основная операция; аргументы передаются вместе с селектором |
:= | присваивание переменной ссылки на объект |
^ | возврат объекта из метода |
литералы и | … | | числа, строки, символы; список имён временных переменных |
Остальное — соглашения библиотеки классов. Подробнее о философии "всё — объект" — в Философия и принципы.
Аналогия с веб-сервером
Обмен сообщениями можно сравнить с HTTP: каждый объект ведёт себя как сервер, отвечающий на запросы. Он может вернуть готовый ответ, перенаправить запрос другому объекту (как прокси), преобразовать запрос или собрать ответ из аргументов сообщения. Если подходящего метода нет, среда посылает получателю doesNotUnderstand: — по смыслу это близко к странице "404" для несуществующего URL.
Пример — гласные в строке
Классический учебный фрагмент (стиль Smalltalk — короткие выражения, цепочки сообщений):
| aString vowels |
aString := 'This is a string'.
vowels := aString select: [:aCharacter | aCharacter isVowel].
Строке посылается сообщение select: с аргументом — блоком (анонимная функция с одним параметром). Метод select: объявлен в абстрактном классе Collection (предок String) — он создаёт коллекцию того же "вида", перебирает элементы через do: и добавляет те, для которых блок вернул true после сообщения ifTrue:.
Тот же select: работает и с другими коллекциями — например, с прямоугольниками и точкой:
| rectangles aPoint collisions |
rectangles := OrderedCollection
with: (Rectangle left: 0 right: 10 top: 100 bottom: 200)
with: (Rectangle left: 10 right: 10 top: 110 bottom: 210).
aPoint := Point x: 20 y: 20.
collisions := rectangles select: [:aRect | aRect containsPoint: aPoint].
Ключевые слова
Синтаксис Smalltalk — один из самых минималистичных в истории.
В ядре языка всего несколько зарезервированных имён — true, false, nil, self, super, thisContext. На практике это псевдопеременные, а не операторы вроде if или return.
Нет специальных конструкций для циклов — есть методы вроде 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'; cr ].
Как можно обратить внимание, нет ключевого слова if. Так же нет for, while, class, function, return — всё это реализуется через сообщения и объекты. В данном случае, x>0 - это выражение, которое возвращает объект true или false. Мы посылаем этому объекту сообщение ifTrue.
true выполняет блок, а false игнорирует. То есть условие — это поведение объекта.
Интерактивное демо — пошаговый цикл на примере JavaScript (
for,while). В Smalltalk нетfor— повторение черезtimesRepeat:,to:do:,whileTrue:; демо показывает классическую модель итераций. Обобщённо: циклы в коде.
Play ITЗагрузка интерактивного демо…
То же самое с циклами:
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 строится вокруг трёх форм посылки сообщений.
Унарные сообщения
У них нет параметров, имя - одно слово, и они имеют высокий приоритет. Пример:
42 factorial
'hello' size
Date today
Здесь объект 42 отправляет себе сообщение factorial.
Бинарные сообщения
Это что-то вроде операторов из символов +, -, *, /, >, <, =, , и др, но это просто сообщения.
3 + 4
'hello' , ' world' "конкатенация строк"
x > 0
3 + 4 — объект 3 получает бинарное сообщение + с аргументом 4 (селектор — +, не "число 4 как имя метода").
Свои бинарные селекторы тоже возможны (один или два символа), но в учебных примерах чаще используют стандартные +, //, @ и т.д.
Ключевые сообщения
Имя сообщения разделено на части, каждая из которых заканчивается на : (двоеточие), а каждая часть принимает один аргумент:
'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"
Названия знаков по-английски и по-русски: Знаки препинания и символы в IT.
Знаки препинания
Знаки препинания тоже имеют свои значения:
. (точка) это разделитель выражений. Как точка с запятой в C/Java. К примеру:
x := 5. y := 10
: (двоеточие), как мы выявили ранее, указывает на параметр в ключевом сообщении.
` (апостроф) разделяет объявление переменных в блоке или методе.
[] (квадратные скобки) определяют блок кода.
() (круглые скобки) группируют выражения.
:= (двоеточие и равно) определяют присваивание:
count := 0
" (двойные кавычки) определяют комментарий.
Как можно заметить, нет фигурных скобок, нет ; в конце строк, нет return.