Ruby and Its Neighbors: Smalltalk
Smalltalk оказал значительное влияние на Ruby, хотя его синтаксис практически не перешёл в Ruby. Основное влияние проявилось в объектно-ориентированных принципах, особенно в идее, что все данные являются частью объектной системы. В отличие от Perl, автор статьи работал с Smalltalk несколько лет и считает его одним из любимых языков, хотя вряд ли будет использовать его снова.
Smalltalk возник в Xerox PARC, той же команде, что создала графический интерфейс, Ethernet и лазерный принтер. В 80-90-х годах Smalltalk был коммерческим продуктом (ObjectWorks/VisualWorks), широко использовался в авиационной промышленности и для проектов, лёгших в основу экстремального программирования. В 1995 году бывшие сотрудники Xerox PARC в Apple выпустили Squeak — открытую реализацию Smalltalk, написанную в основном на самом себе, что обеспечило её лёгкую переносимость. В отличие от большинства современных языков, Smalltalk развивался независимо от Unix/C, представляя собой собственную операционную систему с уникальным синтаксисом и интегрированной средой разработки.
Комментарии (124)
- Smalltalk-80 и его наследие: образ системы (image) как способ распространения состояния, но его нет в современных системах, что делает Smalltalk-80 уникальным.
- Проблема в том, что Smalltalk-80 не имеет синтаксиса, который бы соответствовал современным ожиданиям, и это делает его непривлекательным для новых разработчиков.
- Ruby унаследовал объектную модель Smalltalk, но не его среду разработки, что делает Smalltalk-80 уникальным в своем роде.
- Сообщество Smalltalk активно разрабатывает Pharo и другие современные реализации, но они не могут конкурировать с уже устоявшимися языками, потому что не имеют большой экосистемы.
- Проект, который начинал как Smalltalk-80, теперь может быть выжившимся только как встроенный язык в некоторых проприетарных системах.
Computer science courses that don't exist, but should (2015) 🔥 Горячее 💬 Длинная дискуссия
Статья представляет собой воображаемый список курсов компьютерных наук, которых не существует, но должны. Автор предлагает такие курсы, как "Отказ от объектно-ориентированного программирования", где студенты изучат переменные вне иерархии объектов и "функции", а также "Классические исследования программного обеспечения", посвященные исторически значимым продуктам вроде VisiCalc и Zork. Особый интерес представляет курс "Написание быстрого кода на медленных языках", где учат писать на Python, который по производительности может превзойти C++, и "Пользовательский опыт командной строки", анализирующий принципы UX для консольных программ.
Автор Джеймс Хейг, бывший программист, проектирующий видеоигры с 1980-х, подчеркивает, что программирование — это не технология ради самой технологии, а возможность реализации идей. Он также предлагает курс "Одобрения программистского ума", изучающий навязчивые темы, которыми часто увлекаются разработчики: форматирование кода, таксономия, системы типов и чрезмерное дробление проектов.
Комментарии (195)
- Обсуждение показало, что академическая программа CS часто не охватывает такие темы, как история ПО, философия вычислений, практика командной работы и даже базовые навыки работы в консоли.
- Участники подчеркнули, что курсы вроде "Unlearning OOP" и "Classical Software Studies" должны быть обязательной частью образования, но их нет.
- Обсуждение подняло вопрос о том, что не хватает курсов по фундаментальным навыкам, таким как CI/CD, системное администрирование и работа с консолью.
- Было отмечено, что студенты часто не получают практических навыков, необходимых для работы в отрасли, и что курсы по таким темам как "как писать быстрый код на медленных языках" или "как не страдать от внутренних конфликтов в команде" отсутствуют.
- В конце концов, обсуждение подтолкнуло к тому, что вместо того, чтобы учить студентов тому, как создавать и поддерживать сложные системы, которые уже работают, они вместо этого вынуждены изучать фреймворки, которые могут исчезнуть через 5 лет.
Clojure's Solutions to the Expression Problem
Clojure и «проблема выражений»
- Проблема: добавлять новые типы данных и новые операции без перекомпиляции всего кода и без нарушения существующих вызовов.
- ООП-языки решают лишь половину: легко новый класс, трудно новый метод.
- Функциональные языки наоборот: легко новая функция, трудно новый вариант данных.
Как Clojure объединяет лучшее
-
Протоколы
- Описывают набор методов без привязки к типу.
- Реализуются для любого существующего класса поздно, «извне».
- Компилируются в обычный Java-интерфейс, быстрый вызов.
-
Мультиметоды
- Выбор реализации по произвольной функции-диспетчеру (тип, значение, метаданные).
- Позволяют «разрезать» иерархию по другим осям, не только по классу.
-
Records и types
defrecordсоздает неизменяемую структуру с заранее известными полями и автоматическим доступом по ключам как к карте.deftypeдаёт полный контроль, поля хранятся примитивно, без лишних обёрток.
-
reify
- «Анонимный класс» на Clojure: создаёт объект, реализующий нужные протоколы/интерфейсы, без отдельного файла.
Практический итог
- Новый тип →
defrecord/deftype+ реализация нужных протоколов. - Новая операция → добавляем метод в протокол и реализуем для всех существующих типов.
- Старый клиентский код не трогается, компиляция не требуется.
Комментарии (16)
- Протоколы в Clojure теперь умеют диспатчиться по метаданным, а не только по типу.
- Участники скучают по временам, когда посты о продуктивности были про REPL, функциональность, композабельность и иммутабельность.
- Поделились ссылкой на демонстрацию решения «expression problem» в Clojure.
- Люди недооценивают готовые решения вроде Datomic и вместо этого пишут свои костыли.
- Один из участников вспомнил, как в 2013–15 писал тесты на Clojure и расширял фреймворк для Selenium.
The Expression Problem and its solutions (2016)
Проблема выражений и её решения
Проблема выражений: нужно добавлять новые типы данных и новые операции без изменения старого кода.
В ООП-языках легко добавлять типы (наследование), но сложно — операции (менять интерфейс).
В функциональных языках наоборот: легко добавлять функции, сложно — варианты данных.
Пример на C++
Базовый класс Expr с двумя методами: Eval() и ToString().
Новый тип — просто новый класс-наследник.
Новая операция — правим базовый класс и все наследников, нарушая OCP.
Функциональный подход (Haskell)
Типы данных и функции разведены:
data Expr = Constant Double | BinaryPlus Expr Expr
eval (Constant x) = x
eval (BinaryPlus a b) = eval a + eval b
Добавить операцию легко: пишем новую функцию.
Добавить вариант Expr — правим сам тип и все функции, pattern-match’и которых его затрагивают.
Как быть
- Визитор (ООП) — двойная диспетчеризация, но код всё равно растёт.
- Мультиметоды (CLOS, Clojure) — выбор по типу всех аргументов, код не трогается.
- Type-class / протоколы (Haskell, Clojure) — «открытые» функции, реализуемые вне исходного модуля.
- Tagless-final / finally-tagless — выразить язык как набор операций, интерпретаторы добавляются без изменения AST.
Итог: ни один стиль не побеждает; выбираем язык и технику, которая даёт нужную сторону расширяемости.
Комментарии (69)
- Суть проблемы: нужно добавлять новые типы данных и новые операции без переписывания старого кода и без потери статической безопасности.
- Классический подход: O·T матрица — каждая операция реализуется для каждого типа; при росте O или T взрывается boilerplate.
- Языковые решения:
– трейты/impl в Rust, typeclass в Haskell, протоколы в Swift позволяют добавлять операции «сбоку»;
– enum (суммарные типы) упрощают добавление операций, но затрудняют новые варианты данных. - Продвинутые техники: tagless-final, object algebras, data types à la carte, multiple dispatch, visitor-pattern.
- Ограничения: «сиротское правило», отсутствие виртуальных extension-методов, необходимость заранее знать все комбинации при раздельной компиляции.
- Вывод: идеального языка нет; выбор зависит от того, что важнее — расширять типы или операции — и насколько нужна статическая типизация.
Object-oriented design patterns in C and kernel development 💬 Длинная дискуссия
Разработка собственной ОС освобождает от ограничений коллективной работы и позволяет экспериментировать с необычными паттернами. Вдохновлённый статьёй LWN «Object-oriented design patterns in the kernel», я реализовал все сервисы ядра через «виртуальные таблицы» (vtables) — структуры с указателями на функции, обеспечивающие полиморфизм на чистом C.
Базовая идея
struct device_ops {
void (*start)(void);
void (*stop)(void);
};
struct device {
const char *name;
const struct device_ops *ops;
};
Разные устройства регистрируют свои реализации ops, а вызывающий код работает с единым интерфейсом. Таблицу можно менять на лету без изменения клиентов.
Применение в моей ОС
- Сервисы: сетевой менеджер, оконный сервер и др. описываются структурой
struct service_ops { void (*start)(void); void (*stop)(void); void (*restart)(void); };
Позволяет из терминала запускать/останавливать потоки без хардкода.
- Планировщик: интерфейс
yield, block, add, nextреализуется разными стратегиями (round-robin, SJF, FIFO). Политику можно заменить без пересборки ядра. - Файлы: как в Unix, «всё есть файл». Сокеты, устройства и обычные файлы предоставляют одинаковые
read/write, скрывая сложность реализации.
Модули ядра
Такой подход легко расширяется динамически загружаемыми модулями-драйверами, как в Linux.
Комментарии (160)
- Обсуждение показывает, что в ядре Linux и других проектах на C давно применяют «объектно-ориентированные» приёмы через структуры с указателями на функции (таблицы виртуальных методов).
- Некоторые считают это удобным и экономным по памяти, другие — источником проблем с читаемостью, отладкой и оптимизацией.
- Упоминаются готовые микро-фреймворки (co2, carbon) и примеры из tmux, где такие паттерны уже используются.
- Спор идёт о необходимости явного параметра this: одни ценят прозрачность, другие — «сахар» неявного this в C++/Java.
- Вопрос «почему бы не перейти на C++/другой язык» сводится к контролю над памятью, отсутствию «магии» и возможности оставаться на C ради производительности и простоты.
Objects should shut up 🔥 Горячее 💬 Длинная дискуссия
—
Комментарии (357)
In safety industries, particularly aviation, "alarm fatigue" is a really big deal. You recognize that pilots have limited situational bandwidth, and you REALLY don't want to be bugging them about things you can avoid. I worked in collision avoidance systems (TAS/TCASI/TCASII), an