Функциональное программирование: состояние или переназначение - PullRequest
13 голосов
/ 09 декабря 2008

Мне нужна помощь, чтобы разобраться в разнице между моим текущим представлением о состоянии ООП и тем, как это было бы сделано на функциональном языке, таком как Haskell или Clojure.

Чтобы использовать избитый пример, допустим, мы имеем дело с упрощенными объектами / структурами банковского счета / чем угодно. В языке ООП у меня был бы некоторый класс, содержащий ссылку на BankAccount, который имел бы переменные экземпляра для таких вещей, как процентная ставка, и методы, такие как setInterestRate (), которые изменяют состояние объекта и обычно ничего не возвращают. Например, Clojure, у меня была бы структура банковского счета (прославленная хэш-карта) и специальные функции, которые принимают параметр банковского счета и другую информацию и возвращают новую структуру. Таким образом, вместо изменения состояния исходного объекта, у меня теперь есть новый, возвращаемый с желаемыми изменениями.

Так ... что мне с этим делать? Перезаписать, какая переменная ссылалась на старый банковский счет? Если да, то имеет ли это преимущества перед подходом ООП с изменением состояния? В конце концов, в обоих случаях кажется, что есть переменная, которая ссылается на объект с необходимыми изменениями. Отсталый, как я, у меня есть только смутное представление о том, что происходит.

Надеюсь, это имело смысл, спасибо за любую помощь!

Ответы [ 4 ]

11 голосов
/ 09 декабря 2008

В чисто функциональном стиле вы никогда не перезапишете какую-либо переменную.

Аналогия была бы с пространством-временем в физике. Если вы рассматриваете мир как 3d, то у объектов нет фиксированных позиций - они движутся со временем. Для того, чтобы математика влияла на физический мир, мы поэтому добавляем измерение времени и рассматриваем значения различных свойств в определенное время. При этом мы превратили объекты нашего исследования в константы. Точно так же в программировании есть концептуальная простота, которую нужно иметь при работе с неизменяемыми значениями. Объекты с идентичностью в реальном мире могут быть смоделированы как последовательность неизменяемых значений (состояния объекта при увеличении времени), а не как одно значение, которое изменяется.

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

Я отмечу, что Clojure, в отличие от Haskell, не является чистым, и вы можете обновлять переменные, как вы предлагали. Если вы обновляете только несколько переменных на высоком уровне, вы все равно, вероятно, будете пользоваться многими из концептуальных преимуществ простоты функционального программирования.

8 голосов
/ 10 декабря 2008

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

updatePortfolio :: Request -> Portfolio -> Portfolio

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

readRequest :: IO Request  -- an action that, when performed, reads a Request with side effects

main :: Portfolio -> IO ()  -- a completely useless program that updates a Portfolio in response to a stream of Requests

main portfolio = do req <- readRequest
                    main (updatePortfolio req)

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

Это очень забавный пример, но я надеюсь, что он даст вам представление о функциональном языке.

4 голосов
/ 09 декабря 2008

Итак ... что мне с этим делать? Перезаписать, какая переменная ссылалась на старый банковский счет?

Да

Если да, то имеет ли это преимущества перед подходом ООП с изменением состояния?

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

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

Кроме того, мы можем сэкономить место, используя данные из другой структуры, из которой мы копируем. Классический пример - при добавлении элемента в начало списка. Если у нас есть указатель на второй элемент и указатель на первый элемент, мы можем ссылаться на оба списка только с размером первого (см. Ниже). Без неизменности мы не можем этого гарантировать.

        b__
           |  
a -> [6|] -+-> [5|] -> [4|] -> [3|] -> [2|] -> [1|x]
1 голос
/ 10 декабря 2008

Посмотрите на Haskell, который является чисто функциональным языком - он не имеет никакого переназначения вообще, а также никаких других побочных эффектов: для выполнения IO в монаде IO конструкция фактически заменяет RealWorld новым экземпляром мира, который имеет, например, новый текст, отображаемый в консоли.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...