Побочные эффекты в замыканиях, они все еще чисто функциональны? - PullRequest
14 голосов
/ 16 ноября 2008

Будучи относительно новым для функционального программирования, я трачу много энергии на размышления: «Это функциональный способ делать что-то?» Очевидно, что рекурсия и итерация довольно просты, и очевидно, что рекурсия - это функциональный способ выполнения задач. Но возьмем замыкания, например. Я узнал о замыканиях с помощью Lisp и понимаю, что замыкания представляют собой сочетание функции и среды (очень похоже на состояние и поведение). Например:

(let ((x 1))
           (defun doubleX()
              (setf x (* x 2))))

Здесь у нас есть функция doubleX, которая была определена в среде переменной x. Мы могли бы передать эту функцию другим функциям, а затем вызвать ее, и она все равно сможет ссылаться на переменную x. Функция может продолжать ссылаться на эту переменную, даже если она вызывается вне среды, в которой переменная была определена. Многие из примеров закрытия, которые я видел, выглядят так. Где setf используется для изменения значения лексической переменной. Это смущает меня, потому что:

1.) Я думал, что Сетф был злом. Главным образом потому, что это вызывает побочные эффекты и, по-видимому, они также являются злом.

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

Может быть, я просто не понимаю замыкания. Кто-нибудь может мне помочь?

Ответы [ 3 ]

12 голосов
/ 16 ноября 2008

Вы правы, использование замыканий для управления состоянием не является чисто функциональным. Lisp позволяет программировать в функциональном стиле, но не заставляет вас. Я на самом деле предпочитаю этот подход, потому что он позволяет мне найти прагматический баланс между чисто функциональным и удобным изменением состояния.

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

8 голосов
/ 16 ноября 2008

Затворы - это объекты бедняков (и наоборот), см.

Когда использовать закрытие?

и мой ответ в нем. Поэтому, если вы намерены использовать побочные эффекты для управления состоянием в вашем не-OO-приложении, замыкания через изменяемые состояния действительно являются простым способом сделать это. Неизменные альтернативы являются «менее злыми», но 99,9% языков предлагают изменчивое состояние, и все они не могут ошибаться. :) Изменяемое состояние полезно при разумном использовании, но оно может быть особенно подвержено ошибкам при использовании с замыканиями и захватом, как показано здесь

О лямбдах, захвате и изменчивости

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

В большинстве случаев с замыканиями вы просто закрываете значения или неизменяемое состояние и «не замечаете», что делаете это.

2 голосов
/ 26 ноября 2008

Common Lisp и Scheme не являются чисто функциональными. Clojure в основном функционален, но все же не чисто. Хаскелл - единственный из известных мне языков, который является чисто функциональным, я даже не могу назвать имя другого.

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

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

...