Smalltalk и лямбда-исчисление занимают важное место в истории и развитии программирования. Несмотря на то что Smalltalk зачастую ассоциируется с объектно-ориентированным подходом, а лямбда-исчисление - с чисто функциональной моделью вычислений, между ними существует глубокая философская и техническая связь. Понимание этой связи позволяет лучше разбираться в природе вычислений, а также расширяет взгляды на проектирование языков программирования и спецификацию типов. Smalltalk - один из первых объектно-ориентированных языков программирования, в котором всё представлено в виде объектов. Объект в Smalltalk - это сущность, которая получает сообщения с именами и аргументами и на них реагирует.
Более того, объекты в нём тесно связаны между собой посредством обмена сообщениями, что создает удивительно чистую и элегантную модель коммуникации в программных системах. Но как же выразить в такой модели общепринятые структуры данных, например, булевы значения или числа? Булевые объекты в Smalltalk реализованы как объекты, которые реагируют на сообщение ifTrue:ifFalse:. Это сообщение принимает два аргумента, которые должны быть блоками кода - так называемыми замыканиями или безымянными функциями. Объект true выполняет первый блок, объект false - второй. Такое поведение можно выразить коротко в виде уравнений: true при получении сообщения ifTrue:ifFalse: запускает первый блок, а false - второй.
Данная концепция удивительно напоминает классическую реализацию булевой логики в лямбда-исчислении, где истина и ложь представлены функциями, выбирающими первое или второе значение соответственно. Что касается чисел, Smalltalk традиционно не выделяет в языке отдельного типа для натуральных чисел, однако одна из возможностей работы с целыми числами - сообщение timesRepeat:. Оно принимает блок кода и выполняет его столько раз, сколько обозначено числом. Отрицательные числа в Smalltalk не повторяют выполнение блока, что косвенно отражает идею о том, что timesRepeat: наиболее естественно подходит для моделирования натуральных чисел, отражающих количество повторений действия, неотрицательное по своему смыслу. Некоторые разработчики предлагают более формальные конструкции для натуральных чисел в Smalltalk.
Предлагается, например, сообщение from:iterate:, которое принимает два аргумента - начальное значение и блок, осуществляющий следующий шаг итерации. Ноль, в этом контексте, возвращает начальное значение, а любое натуральное число n (большое нуля) содержит ссылку на предшественника и выполняет итерацию, вызывая block с результатом вычисления from:iterate: для предшественника. Такой подход может рассматриваться как объектно-ориентированная абстракция, аналогичная рециркуляции и рекурсии в функциональных языках. Объявленное timesRepeat: может быть реализовано через from:iterate:, что подчеркивает их взаимозаменяемость и глубокую связь в концептуальном плане. Эта ситуация тесно связана с так называемыми энкодингами Чёрча - формализмом, позволяющим выразить числовые и булевы значения с помощью чистых функций без внутренних состояний.
В лямбда-исчислении всё строится на функциях с одним аргументом. Булевы значения представлены функциями, которые принимают два аргумента и выбирают один из них: true - возвращает первый аргумент, false - второй. Эта абстракция - чрезвычайно мощный концепт, который позволяет реализовать ветвление без встроенных конструкций языка. Аналогично, натуральные числа встречаются как функции, которые принимают два аргумента - начальное значение и функцию, которую нужно применить n раз. Так, ноль - это просто функция, возвращающая начальное значение, а succ (преемник) - функция, которая применяет переданную функцию один раз больше.
Сходство определений булевых и числовых объектов в Smalltalk и лямбда-исчислении указывает на более фундаментальную идею - объекты Smalltalk можно рассматривать как частные случаи лямбда-функций, а функции лямбда-исчисления - как объекты, реагирующие на сообщения. В одном интерпретируемом виде лямбда - это объект, понимающий сообщение value:, которое имеет один аргумент. При получении такого сообщения объект выполняет преобразование аргумента и возвращает результат. Обратное тоже справедливо: объект может быть представлен как лямбда, применяющаяся к сообщению с именем и полезной нагрузкой, осуществляя поиск обработчиков и применяя соответствующие функции к аргументам. Переменные, захваченные лямбда-выражением во время его создания, в объектной интерпретации соответствуют полям объекта или его состоянию, которые скрыты от внешнего мира и доступны только через методы или сообщения.
Это чудесное отображение подчеркивает глубинную взаимосвязь между двумя парадигмами: объектно-ориентированным и функциональным программированием. Основы теории типов оказываются полезными для понимания этой связи. Объекты и лямбды могут рассматриваться как коиндуктивные типы, что связано с понятием бесконечных структур и финальных когнатов в теории категорий и типах данных. Такое обоснование позволяет утверждать, что выбор между объектами и функциями как фундаментальной единицей вычислений - это, скорее, вопрос стиля и модели, чем принципиального различия. Возникает также вопрос об отсутствии в Smalltalk анонимных объектов, которые могли бы расширить модель и предложить альтернативные механизмы абстракции.
Язык строится на том, что все объекты создаются путем отправки сообщений классам, которые сами являются объектами, создавая взаимозависимую структуру. Возможно, введение класс-лесс или анонимных объектов позволило бы упростить и сделать более выразительной модель, одновременно сохраняя мощь и гибкость объектной системы. В целом Smalltalk впечатляет своей лаконичностью и ясностью синтаксиса. Он может рассматриваться как удобочитаемая оболочка для понятий, которые впервые явственно описаны через энкодинги Чёрча и лямбда-исчисление. Такая перспектива на Smalltalk открывает новые горизонты для изучения языка и понимания фундаментальных принципов программирования.
Эти соображения не только обогащают теоретические знания, но и стимулируют разработку новых языковых конструкций и подходов в программировании будущего. .