Почему хорошо не полагаться на изменение состояния?

16

Этот вопрос возникает из вопроса /software/25569/is-haskell-worth-learning

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

Почему?

Я видел, как кто-то сравнивал это с тем, чтобы печатать только левой рукой, или, возможно, закрыть глаза на день и просто положиться на прикосновение. Конечно, это еще не все?

Это относится к аппаратному доступу к памяти или к чему-то еще, что приводит к значительному увеличению производительности?

ocodo
источник
2
Хаскель академичен. Я бы посмотрел несколько лекций Рича Хикки о Clojure - там он приводит убийственные прагматические аргументы (аналогично 3 пунктам Хавьера, но также и в простой форме).
Работа
2
Да, см. Clojure.org/state
LennyProgrammers
6
Тот факт, что Haskell является «академическим», не означает, что это непрактично или не прагматично.
Тихон Джелвис

Ответы:

17

у меня по крайней мере три больших преимущества:

  1. это делает программы более близкими к математическим выражениям. В математике xне меняется, вы просто не знаете, что это такое, пока не решите уравнение.

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

  3. Параллельный код не должен синхронизироваться для доступа к неизменяемым данным, поэтому параллелизм повышается как в системах с общей памятью SMP (все современные многоядерные системы), так и в слабо связанных кластерах.

Хавьер
источник
3
Предложения функционального программирования ставят наибольшую нагрузку № 3, то есть упрощают параллельное программирование.
MCHL
4
@Mchl: По моему опыту, они больше всего акцентируют внимание на «легче понять и рассуждать», что соответствует 1. Хотя я полагаю, что это может отличаться в разных языковых сообществах.
sepp2k
+1, очень полный ответ. @ sepp2k: оба важны, я думаю. Рассуждение о программе - это то, что мы делаем ежедневно, и это правда, что если вам не нужно проверять, что состояние не изменилось в какой-то глубоко скрытой функции, гораздо проще читать только высокоуровневые методы и узнавать, что происходит. Аппаратно: поскольку мы все больше и больше переходим к многоядерным и многопроцессорным системам (хотя, думаю, пройдет много времени, прежде чем домашние компьютеры получат мультипроцессоры), использование языка, облегчающего параллельное программирование, является преимуществом.
Матье М.
Хороший ответ, я предположил, что ответ №2 был, и хотя я часто читал о преимуществах многопроцессорности, это редко указывалось как явно отличная работа.
ocodo
1
@Yttrill, функциональные языки не уникальны в своей зависимости от сборки мусора, и сборка мусора намного проще, когда вы имеете дело с неизменяемыми данными. Выполнение любого вычисления на основе архитектуры команд означает изменение состояния, но это не означает, что функциональные языки как-то сложнее распараллелить. Функциональные языки качаются в параллелизме; поиск данных параллельно Haskell, это функция, которую было бы практически невозможно добавить к императивному языку.
dan_waterworth
4

Вот еще одно преимущество: уменьшенное сцепление. Если у вас есть такой код:

 function doStuff(x) { return x + y;}

а в других местах у вас есть:

 function doOtherStuff(x) { y++; return y + x;}

тогда две функции зависят неявно . Нет простого способа сказать, что на вызов doStuffвлияет вызов doOtherStuff. Без изменяемого состояния вы должны сделать соединение явным.

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

Тихон Джелвис
источник
+1. Многие опытные программисты знают, что не следует писать код, подобный описанному выше, и переход от «изменяемого состояния в этой ситуации не так уж и много» - это не такой уж большой шаг, чтобы «серьезно снизить количество изменяемого состояния и писать более функционально», но это шаг что неутешительно мало кто делает.
dan_waterworth
2

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

Суть в приведенном выше анализе заключается в том, что он бесполезен без комментариев, объясняющих намерение, инварианты и семантику: это может быть трудно интерпретировать, и может быть трудно проверить, соблюдается ли семантика в реальном коде.

Этот ответ в основном является расширением точки 1 Хавьера.

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

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

Yttrill
источник
Смешивание функционального и императивного программирования - это именно то, что делает Haskell - нормальные функции являются функциональными, в то время как вычисления с состоянием могут быть выражены с помощью нотации, которая позволяет вам тщательно изолировать и контролировать изменяемое состояние (среди прочего). Вот почему язык с наиболее практичной реализацией STM - это Haskell .
Тихон Джелвис
Do-нотация сама по себе не имеет ничего общего с вычислениями с учетом состояния . Do-нотация - это просто более простой синтаксис поверх конвейеров монадических функций. Вычисление с учетом состояния - это всего лишь одна из вещей, которые могут быть выражены с помощью монад и, следовательно, do-обозначений.
Джонатан Стерлинг
Смешанное функциональное и императивное программирование - это то, что делает почти каждый существующий язык. Haskell, возможно, предоставил способ изолировать части с состоянием, но это не делает его надлежащим соединением: благотворительность больше похожа на то, как она должна быть (ИМХО).
Иттрилл
2

Как пишет автор Siege , СУБД, написанной на Haskell, некоторые могут назвать мой взгляд на изменчивое состояние противоречивым. Я надеюсь показать иначе.

Цель изменяемого состояния - описать текущее состояние, в котором находится система. Допустим, у вас есть блог, и он поддерживается базой данных, база данных описывает сообщения, которые есть в вашем блоге в тот момент, когда вы запрашиваете Это. Сколько сообщений существует прямо сейчас?

Сравните это с неизменным состоянием, которое используется для передачи фактов. Сколько постов было 12 августа?

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

dan_waterworth
источник