Пару месяцев назад один из моих коллег разместил в нашем чате Slack следующий маленький коан (источник) и упомянул, что сам еще не достиг просветления:

Почтенный мастер Qc Na гулял со своим учеником Антоном. Надеясь побудить мастера к дискуссии, Антон сказал: «Мастер, я слышал, что предметы - это очень хорошая вещь - правда ли это?» Qc Na с жалостью посмотрел на своего ученика и ответил: «Глупый ученик, объекты - всего лишь закрытие для бедняков».

Наказанный, Антон попрощался с хозяином и вернулся в камеру, чтобы изучить закрытие. Он внимательно прочитал всю серию статей «Lambda: The Ultimate…» и ее кузенов и реализовал небольшой интерпретатор Scheme с объектной системой на основе замыканий. Он многому научился и с нетерпением ждал возможности сообщить своему хозяину о своих успехах.

Во время следующей прогулки с Qc Na Антон попытался произвести впечатление на своего учителя, сказав: «Учитель, я усердно изучил этот вопрос и теперь понимаю, что объекты действительно являются закрытием для бедняков». Qc Na в ответ ударил Антона палкой и сказал: «Когда ты научишься? Закрытие - цель бедняков ".

В этот момент Антон просветился.

Я никогда не упускал возможности надеть преподавательскую шляпу. Я придумал следующий небольшой пример и сопровождающее его объяснение и теперь, наконец, нашел время, чтобы превратить его в сообщение в блоге.

Основная посылка

Замыкание «закрывается» над окружающей средой, поэтому оно сочетает в себе состояние (указанное окружение) и поведение (то есть то, что на самом деле происходит при выполнении). По сути, это объект с единственным методом, которым в случае Ruby является call (также имеет псевдоним . ()). Звучит знакомо? Должен, потому что в конце концов объект также состоит из состояния (переменных экземпляра) и поведения (методов).

Пример кода

Глядя на make_counter, назначение начального значения для i устанавливает среду, которая будет «закрыта» . Нетрудно понять, как это по существу соответствует инициализации объекта. Затем мы возвращаем лямбда, которая имеет начальное состояние (i == 0) и метод (.call) для управления этим состоянием для получения следующего числа. Для пользователей нашего кода counter1 и counter2, которые были созданы с помощью make_counter, ведут себя точно так же, как counter3 и counter4, которые являются экземплярами класса Counter.

Сомневаться

В этот момент мой коллега начал понимать, к чему все это ведет, но все еще сомневался:

о, понятно, но это функция, а не объект ...

Это, очевидно, верно, но помимо лямбда-выражений, являющихся обычными объектами в Ruby, наши счетчики - это больше, чем «просто» функции. Подобно объектам, они имеют внутреннее состояние (переменная закрытого i) и способ изменения указанного состояния (вызов лямбда-выражения). Это функционально (без каламбура) эквивалентно объекту, у которого есть переменная экземпляра и метод ее изменения.

Просвещение

Хорошо, я вижу: слегка_smiling_face:

Термин «просветление», конечно, используется здесь в шутку, но мой коллега, наконец, понял эту притчу, и я считаю это еще одной маленькой победой в моем продолжающемся стремлении убедить людей в том, что функциональное программирование и объектно-ориентированное программирование ортогональны, в отличие от скажем, функциональное и императивное программирование.