Smalltalk-80
FROM: Максим Сохацкий
TO: Библиотека Языков Программирования

Сразу хочу сказать, что пост будет абсолютно необъективным и иррациональным, ведь речь сегодня пойдёт об ООП. Если говорить серьезно об ООП или пытаться хотя бы, то в большинстве случаев это всё конечно же третьекультурные феномены за исключением только ООП в зависимых типах от Луки Карделли и, вот, Смоллтолк-80.
Я был рожден в мире, в котором уже существовало функциональное программирование, но ООП еще не было, оно появлялось на моих глазах. Мы обсуждали в курилке: может ли ООП заменить массивы с типизированными указателями на паскале или нет? Когда я наконец понял зачем оно нужно, я понял также и то, что ООП радости мне не принесёт, и через 5 лет рабочей практики я дропнулся с ООП, откатившись к функциональному программированию в 70-е, где и остаюсь до сих пор, а работаю на виртуальной машине созданной в 80-х.
Сейчас же, я хочу воспользоваться возможностью пропиарить Смоллтолк-80 и философию минимализма в индустрии программного обеспечения на его примере, хотя конечно никто не думал, в те времена, что это компактная система.
Если вы помните историю про Стива Джобса, то по легенде он идёт в компанию XEROX PARC (НИИ в Пало Альто), где видит Smalltalk-80 и уходит вдохновишись создавать Apple Macintosh. Таким образом, это не только одна из первых ООП систем, но и первый в мире промышленный графический интерфейс. Алан Кей и ребята уже потом подтянулись, так что простите, упоминаться не будут. Основной автор книг, популяризатор, коммерциализатор и ключевой человек проекта — Адель Голберг. Она не только формальный автор языка Smalltalk-80, но и руководитель проекта.
Технически Smalltalk-80 представляет собой компактную виртуальную машину с примитивами для вызова сообщений, управления стеком и регистрами. Все это линкуется вместе с графическим бит-блиттером и непосредственным обработчиком ввода вывода. В условиях элементной базы прошлого это всё должно помещаться в 64КБ ROM. Поверх этого ядра строится графический интерфейс и пространство из слепка файлов программ, которые загружаются в память последовательно и редактируются в графическом эксплорере, который стал прототипом для файловых навигаторов NeXT, Mac, OCaml. В этом объектном браузере и воркспейсе вы можете редактировать и выполнять любой участок кода системы включая обновление кода планировщика. В системе всегда доступна возможность выйти в дебаггер, переписать мир, и вернуться в контекст, впрочём и его можно подменить. Естественно, поддерживается возможность сохранить контекст всей виртуальной машины и перенести его на другую платформу — фича которая определяет первокультурность окружения тру хакера или просто современного разработчика. В такой первокультурный класс систем входят, пожалуй, только лисп-машины LMI и Symbolics. Даже Эрланг и APL не дотянули до этого класса. Фактически, сегодня такие системы никому не нужны, никто не инвестирует деньги в это (разве что в юникернелы и то в виде жалких унивеситетских грантов), это тайное знание, которое преедается устно от мастера ученику.
При старте системы вы получали десктоп с оконным менеджером, файловым редактором, отладчиком, графическим редактором и базовой библиотекой. Реализовать это всё в таком объеме можно вынеся всю имплементацию на диск, базовый "образ", который представляет из себя вариацию tar архива размером с 1.5МБ (в те времена бобина) в PLAIN TEXT Smalltalk-80.
Долгое время бывшие сотрудники XEROX инвестировали своё время сначала в Squeak, потом начали в Pharo, но системы эти переработаны уже значительно, не обладали аутентичностью XEROX и запустить оригинальные образы было не в чем. В карантин 2020 года Ден Банай решил написать имплементацию ВМ для оригинального смолтолковского образа из НИИ и доступно это прямо на Github — Smalltalk-80! Теперь ИТ археологи имеют возможность запустить оригинальное первокультурное рабочее окружение и посмотреть как оно было в 1980 не только на Tektronix 4406 но и на Windows, Linux и Mac. Возможно даже склепать на ардуино микро-компьютер с видеоадаптером который будет крутить исключительно Smalltalk-80.
До этого момента я думал, что операционные системы прошлого нужно преподавать с BeOS, а юникернелы только с Erlang и OCaml, но сейчас вижу, что Smalltalk-80 вполне мог бы стать упоротой базой для унифицированых окружений, которая могла бы составить конкуренцию на рынке юникернелов (ну или как минимум классифицируется таким образом). Всё в иплементации красиво и на своём месте, вполне допускаю что делался очень близкий порт по духу, хотя автор говорит, что писал по "синей книге", видимо он достиг такого уровня имперсонификации, что его можно назвать соавтором виртуальной машины. Стрипнутый бинарник на маке минимально 120КБ, а на винде все 300КБ. С поправкой на релокейшин таблицы в 64-битных платформах вполне допускаю, что на 8-битной платформе можно засунуть имплементацию в 64КБ. Ну вот ERP на Smalltalk-80 можно писать прямо сейчас :-) Все, кто так или иначе хочет ознакомиться с первокультурным ООП, предлагаю скачать и собрать Smalltalk-80 с гитхаба, и уделить некоторое внимание исходникам — я вот начал и два дня не могу остановиться.
Грамматика
Character = ? Any Unicode character ?;
WhitespaceCharacter = ? Any space, newline or horizontal tab character ?;
DecimalDigit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
Letter = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M"
| "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
| "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m"
| "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z";
CommentCharacter = Character - '"';
Comment = '"', {CommentCharacter}, '"';
OptionalWhitespace = {WhitespaceCharacter | Comment};
Whitespace = (WhitespaceCharacter | Comment), OptionalWhitespace;
LetterOrDigit = DecimalDigit | Letter;
Identifier = (Letter | "_"), {(LetterOrDigit | "_")};
Reference = Identifier;
ConstantReference = "nil" | "false" | "true";
PseudoVariableReference = "self" | "super" | "thisContext";
ReservedIdentifier = PseudoVariableReference | ConstantReference;
BindableIdentifier = Identifier - ReservedIdentifier;
UnaryMessageSelector = Identifier;
Keyword = Identifier, ":";
KeywordMessageSelector = Keyword, {Keyword};
BinarySelectorChar = "~" | "!" | "@" | "%" | "&" | "*" | "-" | "+" | "=" | "|" | "\" | "<" | ">" | "," | "?" | "/";
BinaryMessageSelector = BinarySelectorChar, [BinarySelectorChar];
IntegerLiteral = ["-"], UnsignedIntegerLiteral;
UnsignedIntegerLiteral = DecimalIntegerLiteral | Radix, "r", BaseNIntegerLiteral;
DecimalIntegerLiteral = DecimalDigit, {DecimalDigit};
Radix = DecimalIntegerLiteral;
BaseNIntegerLiteral = LetterOrDigit, {LetterOrDigit};
ScaledDecimalLiteral = ["-"], DecimalIntegerLiteral, [".", DecimalIntegerLiteral], "s", [DecimalIntegerLiteral];
FloatingPointLiteral = ["-"], DecimalIntegerLiteral, (".", DecimalIntegerLiteral, [Exponent] | Exponent);
Exponent = ("e" | "d" | "q"), [["-"], DecimalIntegerLiteral];
CharacterLiteral = "$", Character;
StringLiteral = "'", {StringLiteralCharacter | "''"}, "'";
StringLiteralCharacter = Character - "'";
SymbolInArrayLiteral = UnaryMessageSelector - ConstantReference | KeywordMessageSelector | BinaryMessageSelector;
SymbolLiteral = "#", (SymbolInArrayLiteral | ConstantReference | StringLiteral);
ArrayLiteral = ObjectArrayLiteral | ByteArrayLiteral;
ObjectArrayLiteral = "#", NestedObjectArrayLiteral;
NestedObjectArrayLiteral = "(", OptionalWhitespace,
[LiteralArrayElement, {Whitespace, LiteralArrayElement}], OptionalWhitespace, ")";
LiteralArrayElement = Literal - BlockLiteral | NestedObjectArrayLiteral | SymbolInArrayLiteral | ConstantReference;
ByteArrayLiteral = "#[", OptionalWhitespace,
[UnsignedIntegerLiteral, {Whitespace, UnsignedIntegerLiteral}], OptionalWhitespace,"]";
FormalBlockArgumentDeclaration = ":", BindableIdentifier;
FormalBlockArgumentDeclarationList = FormalBlockArgumentDeclaration, {Whitespace, FormalBlockArgumentDeclaration};
BlockLiteral = "[", [OptionalWhitespace, FormalBlockArgumentDeclarationList, OptionalWhitespace, "|"],
ExecutableCode, OptionalWhitespace, "]";
Literal = ConstantReference | IntegerLiteral | ScaledDecimalLiteral | FloatingPointLiteral
| CharacterLiteral | StringLiteral | SymbolLiteral | ArrayLiteral | BlockLiteral;
NestedExpression = "(", Statement, OptionalWhitespace, ")";
Operand = Literal | Reference | NestedExpression;
UnaryMessage = UnaryMessageSelector;
UnaryMessageChain = {OptionalWhitespace, UnaryMessage};
BinaryMessageOperand = Operand, UnaryMessageChain;
BinaryMessage = BinaryMessageSelector, OptionalWhitespace, BinaryMessageOperand;
BinaryMessageChain = {OptionalWhitespace, BinaryMessage};
KeywordMessageArgument = BinaryMessageOperand, BinaryMessageChain;
KeywordMessageSegment = Keyword, OptionalWhitespace, KeywordMessageArgument;
KeywordMessage = KeywordMessageSegment, {OptionalWhitespace, KeywordMessageSegment};
MessageChain = UnaryMessage, UnaryMessageChain, BinaryMessageChain, [KeywordMessage]
| BinaryMessage, BinaryMessageChain, [KeywordMessage] | KeywordMessage;
CascadedMessage = ";", OptionalWhitespace, MessageChain;
Expression = Operand, [OptionalWhitespace, MessageChain, {OptionalWhitespace, CascadedMessage}];
AssignmentOperation = OptionalWhitespace, BindableIdentifier, OptionalWhitespace, ":=";
Statement = {AssignmentOperation}, OptionalWhitespace, Expression;
MethodReturnOperator = OptionalWhitespace, "^";
FinalStatement = [MethodReturnOperator], Statement;
LocalVariableDeclarationList = OptionalWhitespace, "|", OptionalWhitespace,
[BindableIdentifier, {Whitespace, BindableIdentifier}], OptionalWhitespace, "|";
ExecutableCode = [LocalVariableDeclarationList], [{Statement, OptionalWhitespace, "."}, FinalStatement, ["."]];
UnaryMethodHeader = UnaryMessageSelector;
BinaryMethodHeader = BinaryMessageSelector, OptionalWhitespace, BindableIdentifier;
KeywordMethodHeaderSegment = Keyword, OptionalWhitespace, BindableIdentifier;
KeywordMethodHeader = KeywordMethodHeaderSegment, {Whitespace, KeywordMethodHeaderSegment};
MethodHeader = UnaryMethodHeader | BinaryMethodHeader | KeywordMethodHeader;
MethodDeclaration = OptionalWhiteSpace, MethodHeader, ExecutableCode;
[1]. Smalltalk-80. Bits of History, Words of Advice. Glenn Krasner. 1984.
[2]. Blue Book. Smalltalk-80: The language and its implementation.
Adele Goldberg and David Robson. 1983.