Неизвестный Smalltalk

от автора

Уважаемые читатели хабра. Прежде всего я хочу объяснить что обзор языка Smalltalk делает в корпоративном блоге FLProg. Дело в том что и сама программа FLProg и сайт программы написаны на этом замечательном языке. Его возможности и огромная скорость разработки на нём позволяют мне одному поддерживать и постоянно увеличивать функциональность, как сайта, так и программы. Если интересно как мне это удается – прошу под кат.

Сначала немного предыстории. Девять лет назад я работал в одной промышленной фирме инженером схемотехником. Рисовал схемы, а на досуге писал небольшие программки на дельфях для облегчения суровой инженерной жизни. Программки постепенно начали расходится по коллективу, руководство это заметило и был создан отдел прикладного программирования. В помощь мне был принят на работу профессиональный программист у которого недавно закончился проект. Вот он то и познакомил меня с языком Smalltalk на котором реализовывался его предыдущий проект. Честно скажу, первая реакция от этого языка у меня была отрицательная. Он ломал все мои представления о программировании. Ломка продолжалась месяца три, после чего я влюбился в Smalltalk и до сих пор предпочитаю работать только на нем, хотя за это время пришлось изучить еще несколько других.
Поскольку повествователь из меня не очень я буду широко использовать материалы из русской википедии посвящённой этому языку и оформлять эти материалы как цитаты.
История возникновения языка:

Smalltalk был создан группой исследователей возглавляемой Аланом Кэйем в исследовательском центре XEROX Palo Alto. Первая реализация, известная как Smalltalk-71, была создана за несколько месяцев как результат спора о том, что язык программирования, основанный на идее посылки сообщений, подсказанной Симулой, должен реализовываться на «странице кода». Более поздняя версия, действительно использованная для исследовательской работы, известна сейчас как Smalltalk-72. Его синтаксис и модель исполнения сильно отличались от современного Smalltalk’а, настолько, что его надо рассматривать как другой язык.
После существенных переработок которые зафиксировали несколько сторон семантики выполнения для увеличения эффективности, была создана версия известная как Smalltalk-76. В этой версии добавились наследование, синтаксис более близкий к Smalltalk-80, и среда разработки включающую большинство инструментов знакомых сейчас Smalltalk-ерам.
Первые годы жизни языка описаны в замечательном эссе Кэя The Early History of Smalltalk (HTML, PDF scan).
В Smalltalk-80 были добавлены метаклассы, что делало фразу «всё объекты» истинной путём связывания с индивидуальными классами свойств и поведения (например, поддержки различных способов создания экземпляров). Smalltalk-80 был первой версией доступной за пределами PARC, сначала как Smalltalk-80 Version 1, розданной небольшому количеству компаний и университетов для «экспертной оценки». Позже (в 1983) общедоступная реализация, известная как Smalltalk-80 Version 2, стала доступна как образ (независимый от платформы файл содержащий объекты) и спецификации виртуальной машины.
Сейчас существует две реализации Smalltalk, являющихся прямыми потомками Smalltalk-80. Это Squeak и VisualWorks. Как выглядел Smalltalk-80 можно увидеть на скриншоте. Образ Smalltalk-80 version 2 запущен на Hobbes, виртуальной машине ST-80 реализованной на VisualWorks.
Smalltalk оказал большое влияние на развитие многих других языков, таких как: Objective-C, Actor, Java и Ruby. Многие идеи 1980-х и 1990-х по написанию программ (Class-Responsibility-Collaboration card) и появились в сообществе Smalltalk, такие как шаблоны проектирования (применительно к ПО), экстремальное программирование и рефакторинг. Основатель концепции WikiWiki, Вард Каннингем, входит в сообщество Smalltalk.

На сегодняшний день последняя версия языка – 8.0 выпущенная в сентябре 2014 года. Конкретно я работаю на реализации VisualWorks и рассказ будет посвящен ему.

Smalltalk является объектным языком, поэтому уместно будет вспомнить базовые принципы ООП (в том виде, в каком они были сформулированы Аланом Кеем):
Объект — базовая единица объектно-ориентированной системы.
Объекты могут обладать состоянием.
Посылка сообщения — единственный способ обмена информацией между объектами.
Кроме того, объектная модель Smalltalk построена на классах, а значит:
Каждый объект относится к какому-то классу.
Функциональность объекта определяется его классом (набором его методов).
Классы организованы в иерархию.
Классы наследуют функциональность от предка (или предков).
Вот, по сути, и всё. Хотя можно дополнительно акцентировать некоторые принципы и уточнить другие:
Всё в Smalltalk является объектами. Т.е. вообще всё. Абсолютно всё. Нет ничего, что не являлось бы объектом.
В Smalltalk существует четыре типа действий — посылка сообщения, присваивание, возвращение значения из метода, вызов примитива виртуальной машины.

Немного от себя. Все переменные в объектах являются указателями. Не существует понятия разыменовывания указателя, переменные всегда ссылки на объект. Во вторых Smalltalk – динамический язык. То есть в любую переменную можно положить ссылку на инстанс объекта любого класса. Соответственно нет понятия приведения типа. В третьих, в языке очень продвинутый сборщик мусора, поэтому практически никогда не приходится пользоваться методами уничтожения объекта. В случае, когда на инстанс обекта не остается не одной ссылки – он быстро уничтожается сборщиком, и память освобождается. Конечно это очень упрощенное описание сборщика мусора, но утечки памяти добиться в Smalltalk – е очень тяжело (мне ни разу не удалось).

Архитектура Smalltalk

Любая система Smalltalk (среда разработки, отдельная исполняемая программа и т.д.) состоит из двух частей — виртуальной машины и образа системы.

Виртуальная машина

Виртуальная машина содержит базовую функциональность: обработчик байт-кодов (в виде JIT-компилятора либо интерпретатора), систему управления памятью (со сборщиком мусора) и примитивы. (Примитивы — это функциональность, которую по каким-либо причинам оказалось удобнее или выгоднее реализовать не на Smalltalk, а в виртуальной машине. Обычно это функциональность самого нижнего уровня, типа сложения чисел или отрисовки точки на экране.)

Образ системы

Образ системы (image) — это файл, хранящий все объекты системы Smalltalk между сеансами работы. К этим объектам относятся исполняющиеся программы, созданные объекты, классы и т.д.
Когда система Smalltalk запущена — все действия, разумеется, выполняются в памяти компьютера. Когда же вы выходите из среды — у вас есть выбор, либо сохранять текущее состояние образа системы, либо нет. Если сохраните образ системы, то при следующем запуске вся среда будет восстановлена предельно точно — вплоть до положения окошек и позиций курсоров. Это происходит потому, что сохраняются (а потом восстанавливаются) состояния всех объектов.

Образ системы является основным контейнером информации в Smalltalk — т.е. в Smalltalk не приняты текстовые файлы с исходным кодом. В принципе, их можно создавать, но работать с образом системы гораздо удобнее.

В настоящее время существуют виртуальные машины под Windows x86 (прекрасно работает по Windows x64), Windows x64, Linux 32, Linux 64, Solaris, Mac Oc. Для перехода на другую платформу достаточно к образу системы подложить необходимую виртуальную машину. В коде бывает иногда бывает необходимо предусмотреть разное поведение при специфических вызовах API, но большинство этих вопросов уже решено в базовых классах. Например работа с файловой системой не требует вообще никаких изменений при переходе на любую платформу. У меня возникала необходимость в доработке кода при портировании на Linux только в области работы с Com портом, и то это заняло не больше пары часов.

Это все была теория а теперь к практике.
Большое преимущество Smalltalk в том что нет необходимости устанавливать среду на машину. Вполне можно создать папку на флешке, положить туда три файлика и рабочее место всегда с собой. Работать можно на любой машине, даже на чужой, даже без прав администратора, при этом после извлечения флешки никаких следов работы не остается.

Основное рабочее окружение Smalltalk:

Class Browser

Workspace

В Class Browser — е хранится весь код программы представляющий набор классов проекта и базовых классов языка. В нем происходит основная работа. При этом следует учесть что все классы «живые» то есть в любой момент можно вызвать любой класс, создать его инстанс, провести необходимые изменения. Для всего этого служит Workspace. В его рабочем поле можно написать и выполнить любой код.

Существует много способов программирования, но для себя я выбрал способ «Программирование через дебаг». Я его не придумал, а подсмотрел на слёте программистов смолторкеров, который проходил в Питере лет восемь назад. К сожалению, не помню докладчика. Насколько я знаю другие языки, больше ни в каком другом этот способ не будет возможен. Дело в том, что Smalltalk позволяет остановить выполнение программы в любой момент, внести исправление в код и продолжить выполнение с точки останова. Объяснить это сложно, проще показать.

Для хранения истории проекта желательно иметь базу данных для системы управления версиями. У Smalltalk-ка она реализована на основе базы данных, и может работать практически на всех известных движках базы. У меня на сервере установлена PostgreeSQL, и я могу к ней подключиться из любой точки при наличии интернета. Система хранения версий поддерживает хранение историй кода от самого начала, создание веток кода, мержа разных веток, возможность откатится до любой версии. Кроме того есть возможность просмотра истории изменения любого метода в системе, и загрузки необходимой версии конкретного метода.
Но наличие такой базы не является обязательным. Кроме системы хранения кода в репозитории существует еще ChangeList. В нем автоматически сохраняются все изменения кода с момента начала работы в образе. С его помощью можно восстановить все изменения, например в случае падения системы (такое иногда бывает при уж слишком грубых экспериментах), отключения компьютера, например при выключении питания. При этом есть возможность восстановить только необходимые методы, исключая например ошибочные. Так же как и в случае с системой хранения версий, можно посмотреть все изменения конкретного метода, и при необходимости восстановить любую версию.

Ну и наконец, самые вкусные возможности Smalltalk — ДЕБАГ и РЕФАКТОРИНГ. И я не просто так написал их большими буквами.
Начнем по порядку.
Дебагер встроенный в Smalltalk при ошибке в коде останавливает выполнение программы точке ошибки, позволяет просмотреть стек методов с просмотром содержимого всех переменных, внести изменение в любом из предыдущих методов, изменить содержимое переменных, и запустить выполнение программы с любого из них дальше. Но это то же легче показать чем объяснить.

Как я уже сказал в ролике, это лишь малая часть возможностей для решения ошибок, реализованная в языке. Полное описание этих возможностей, пожалуй потянет на небольшую книжицу.
Рефакторинг. Основные возможности:
Изменение имени любого класса, метода с автоматическим отслеживанием всех вызовов и упоминаний. При этом дается возможность выбора изменяемых методов.
Изменение имени переменной, с автоматическим отслеживанием применения этой переменной в пределах класса. Тут надо пояснить. Все переменные в Smalltalk – приватные. То есть доступны только в пределах класса, в котором они созданы. Внешний доступ только через акцессоры (геттеры и сеттеры). Соответственно методы, которые могут использовать переменную, присутствуют только в пределах одного класса.
Добавление или удаление параметров метода с автоматическим изменением всех вызовов данного метода.
Возможность просмотра всех вызовов метода, а так же просмотр всех упоминаний класса.
Ну и еще много других функций, которые заслуживают отдельного рассмотрения. Так же хочется показать это в работе.

Вот несколько особенностей языка, которые рвут шаблоны программирования на первых этапах знакомства с языком.
Приоритеты математических операций. Их просто нет. Поскольку числа – это такие же объекты, как и всё остальное, то и операции – это обычные методы. И выполняются в обычной последовательности. Например.
а:=1+2*3/4.
Как это выполнится в обычном языке? Насколько я помню (если не прав — поправьте) 2 помножится на 3, потом результат поделится на 4 и прибавится 1.
На смолтолке к 1 добавится 2, результат умножится на 3 и разделится на 4. Это надо просто запомнить и не забывать применять скобки. Что бы выполнить действия в соответствии с результатом на обычном языке необходимо записать так:
a:= 1+(2*3/4).

Коллекции. Коллекции – это аналог массивам в других языках. Их много и разных (Set, OrderedCollection, ByteArray, и т.д). Кстати строка – это то же коллекция, и большинство методов, относящиеся к коллекции применимы и к ней. Первая особенность, в коллекции могут лежать объекты абсолютно разных классов. Вторая, самая непривычная для любого программиста особенность коллекции – индексы начинаются с 1 а не с 0. Соответственно в строке первый символ имеет индекс 1. И в любой коллекции так же. К этому то же надо привыкнуть.
Ну и третье – оновное. Нет примитивов. Все – объекты. Даже Class – это инстанс класса Metaclass.
Metaclass, это инстанс класса Metaclass. Вот такое колечко на самом верху.

Ну и еще немного о вкусностях.
Расчеты без потери точности. Основная проблема во многих языках – потеря точности при делении. Например: 1/3 – это 0.3333 и 3 в периоде. Соответственно в других языках оговаривается количество знаков после запятой, и точность теряется. В Smalltalk эту проблему решили очень красиво. В результате этого деления мы получим число класса Fraction. Оно содержит в себе числитель (1) и знаменатель (3). То есть, как такового деления не будет, а будет дробь. Ничего не теряем. И все последующие операции подчиняются правилам работы с дробями. Когда необходимо получить десятичное число, просто говорим этому объекту asFixedPoint: с нужным числом знаков после запятой и получаем результат. При этом само число остается неизменным, и мы можем продолжать вести расчеты без потери точности. В других языках я такого не встречал.

Сериализация объектной структуры. Мне известны два пакета: BOSS и SIXX.

BOSS – сохраняет в файл любую объектную структуру с сохранением уникальности объектов. Сохраняет очень быстро, в бинарный файл. Сохраняет очень компактно. Есть минусы. При любом изменении в объявлении сохраненных объектов (добавление, удалении переменных, изменении имени переменных и т.д) файл обратно не вычитается. Для моих задач не подошёл.

SIXX — сохраняет в файл любую объектную структуру с сохранением уникальности объектов. Сохраняет медленнее, чем BOSS, и файл получается больше. Файл текстовый, чем то похож на XML. Большой плюсы. В сохранённых классах можно смело удалять, добавлять переменные, нежелательно только менять их имена. Если добавится переменная, то при вычитывании из файла она инициализируется nil – ом. Такое поведение позволило мне в программе открывать проекты сохраненые еще в первых версиях, Хотя с тех пор в проекте очень многое изменилось.

Работа с базами данных. Я пользуюсь пакетом GLORP входящим в базовую поставку. Пакет служит для сохранения базу и восстановления в базу объектов любой сложности. Для сохраняемых в базу объектов необходимо один раз описать способ их сохранения, и потом вся работа с базой будет заключаться в следующем:

1. Получить сессию работы с базой:
session := FLProgRuDescriptorSystem sessionForLogin: FLProgRuDescriptorSystem login.

2.Сохранить или обновить объект в базе
session save:object.

2. Вычитать все объекты из базы определённого класса
collection:=session readManyOff:Foo.

3. Вычитать с условием:
все где значение переменной равно id=100
collection:= session readManyOff:Foo where:[:each| each id=100].

первое где значение переменной равно id=100 и значение isCorrect = true
object:= session readOneOff:Foo where:[:each| (each id=100) &( each isCorrect=true)].
4. Удаление объекта из базы
session delete:object

Я еще не затронул вопросы создания GUI для приложения, вопросы тестирования (встроен пакет SUnit, наверное самое мощное средство тестирования из известных мне языков), деплой приложения и WEB возможности. Но пост и так получился огромный, и если рассматривать ещё и их, то до конца точно никто не дочитает. Если пост Вам понравится, то я постараюсь выбрать время и рассказать об остальном в продолжении.

Если же Вас заинтересовал этот язык то больще о нем вы можете почитать здесь:
Русская википедия посвященная языку ресурсам которой я пользовался

Группа русскоязычных пользователей Smalltalk

Постараюсь ответить на все ваши вопросы к комментариях

ссылка на оригинал статьи http://habrahabr.ru/post/257611/