Недавно я изучал функциональное программирование (в частности, Haskell, но я также прошел через учебные пособия по Lisp и Erlang). Хотя я нашел эти концепции очень поучительными, я все еще не вижу практической стороны концепции «отсутствия побочных эффектов». Каковы практические преимущества этого? Я пытаюсь мыслить функционально, но есть некоторые ситуации, которые кажутся слишком сложными без возможности легкого сохранения состояния (я не считаю монады Haskell «легкими»).
Стоит ли продолжать углубленное изучение Haskell (или другого чисто функционального языка)? Действительно ли функциональное программирование или программирование без сохранения состояния более продуктивно, чем процедурное? Вероятно ли, что я продолжу использовать Haskell или другой функциональный язык позже, или мне следует изучать его только для понимания?
Меня меньше волнует производительность, чем производительность. Поэтому я в основном спрашиваю, буду ли я работать на функциональном языке более продуктивно, чем на процедурном / объектно-ориентированном / чем-то еще.
источник
Чем больше частей вашей программы не имеют состояния, тем больше существует способов соединить части без каких-либо поломок . Сила парадигмы без гражданства заключается не в безгражданстве (или чистоте) как таковом , а в способности, которую она дает вам писать мощные, многоразовые функции и комбинировать их.
Вы можете найти хорошее руководство с множеством примеров в статье Джона Хьюза « Почему функциональное программирование имеет значение» (PDF).
Вы будете уйма более продуктивным, особенно если вы выбираете функциональный язык , который также имеет алгебраические типы данных и сопоставления с образцом (Caml, SML, Haskell).
источник
Многие другие ответы были сосредоточены на производительности (параллелизме) функционального программирования, что, по моему мнению, очень важно. Однако вы специально спросили о производительности, например, можете ли вы запрограммировать то же самое быстрее в функциональной парадигме, чем в императивной парадигме.
На самом деле я нахожу (по собственному опыту), что программирование на F # соответствует моему лучшему мышлению, и поэтому это проще. Я думаю, это самая большая разница. Я программировал как на F #, так и на C #, и в F # гораздо меньше «борьбы с языком», что мне нравится. Вам не нужно думать о деталях в F #. Вот несколько примеров того, что мне действительно нравится.
Например, даже несмотря на то, что F # статически типизирован (все типы разрешаются во время компиляции), вывод типа определяет, какие типы у вас есть, поэтому вам не нужно это говорить. И если он не может этого понять, он автоматически делает вашу функцию / класс / любой другой универсальной. Так что вам никогда не придется писать что-либо общее, все происходит автоматически. Я считаю, что это означает, что я трачу больше времени на размышления о проблеме и меньше на то, как ее реализовать. Фактически, всякий раз, когда я возвращаюсь к C #, я обнаруживаю, что очень скучаю по этому выводу типа, вы никогда не понимаете, насколько это отвлекает, пока вам больше не нужно это делать.
Также в F # вместо написания циклов вы вызываете функции. Это небольшое изменение, но существенное, потому что вам больше не нужно думать о конструкции цикла. Например, вот фрагмент кода, который может пройти и что-то сопоставить (не помню, что именно, это из головоломки Эйлера проекта):
Я понимаю, что сделать фильтр, а затем карту (то есть преобразование каждого элемента) в C # было бы довольно просто, но вы должны думать на более низком уровне. В частности, вам нужно будет написать сам цикл и иметь свой собственный явный оператор if и тому подобное. С тех пор, как я изучил F #, я понял, что мне легче кодировать функциональным способом, где, если вы хотите фильтровать, вы пишете «filter», а если вы хотите сопоставить, вы пишете «map» вместо реализации каждая из деталей.
Мне также нравится оператор |>, который, как мне кажется, отделяет F # от ocaml и, возможно, других функциональных языков. Это оператор конвейера, он позволяет «перенаправить» вывод одного выражения на вход другого выражения. Это заставляет код больше следовать тому, как я думаю. Как и в приведенном выше фрагменте кода, в нем говорится: «Возьмите последовательность факторов, отфильтруйте ее, а затем сопоставьте». Это очень высокий уровень мышления, которого нельзя достичь в императивных языках программирования, потому что вы так заняты написанием операторов цикла и if. Это единственное, чего мне больше всего не хватает, когда я перехожу на другой язык.
В общем, хотя я могу программировать как на C #, так и на F #, мне легче использовать F #, потому что вы можете думать на более высоком уровне. Я бы сказал, что, поскольку мелкие детали удалены из функционального программирования (по крайней мере, в F #), я более продуктивен.
Изменить : я видел в одном из комментариев, что вы просили пример «состояния» на функциональном языке программирования. F # можно написать императивно, поэтому вот прямой пример того, как можно иметь изменяемое состояние в F #:
источник
a |> b (p1, p2)
это просто синтаксический сахар дляb (a, p1, p2)
. Соедините это с правоассоциативностью, и вы получите это.Подумайте обо всех сложных ошибках, на отладку которых вы потратили много времени.
Итак, сколько из этих ошибок произошло из-за «непреднамеренного взаимодействия» между двумя отдельными компонентами программы? (Почти все пронизывающие ошибки имеют такой вид: расы , подразумевающую запись общих данных, зависаний, ... Кроме того, он является общим , чтобы найти библиотеки , которые имеют неожиданное влияние на глобальном состоянии или чтения / записи реестра / окружающую среды и т.д.) Я постулирует, что по крайней мере 1 из 3 «серьезных ошибок» попадает в эту категорию.
Теперь, если вы переключитесь на программирование без сохранения состояния / неизменяемость / чистое программирование, все эти ошибки исчезнут. Вместо этого вы сталкиваетесь с некоторыми новыми проблемами (например, когда вы действительно хотите, чтобы разные модули взаимодействовали с окружающей средой), но в таком языке, как Haskell, эти взаимодействия явно перерабатываются в систему типов, что означает, что вы можете просто посмотреть на тип функция и причина того, какое взаимодействие она может иметь с остальной частью программы.
Это большая победа от «неизменности» ИМО. В идеальном мире мы все разрабатывали бы потрясающие API-интерфейсы, и даже когда все было изменяемым, эффекты были бы локальными и хорошо документированными, а «неожиданные» взаимодействия были бы сведены к минимуму. В реальном мире существует множество API-интерфейсов, которые различными способами взаимодействуют с глобальным состоянием, и они являются источником самых пагубных ошибок. Стремление к безгражданству означает стремление избавиться от непреднамеренных / неявных / закулисных взаимодействий между компонентами.
источник
Одним из преимуществ функций без сохранения состояния является то, что они позволяют выполнять предварительное вычисление или кэширование возвращаемых значений функции. Даже некоторые компиляторы C позволяют явно отмечать функции как не имеющие состояния, чтобы улучшить их оптимизацию. Как отмечали многие другие, функции без сохранения состояния намного проще распараллелить.
Но эффективность - не единственная проблема. Чистую функцию легче тестировать и отлаживать, поскольку все, что влияет на нее, явно указано. А при программировании на функциональном языке появляется привычка делать как можно меньше функций «грязными» (с вводом-выводом и т. Д.). Такое разделение содержимого с отслеживанием состояния - хороший способ разрабатывать программы даже на не очень функциональных языках.
Функциональные языки могут занять некоторое время, чтобы «освоиться», и это трудно объяснить тому, кто не прошел через этот процесс. Но большинство людей, которые настаивают на этом достаточно долго, наконец понимают, что суета того стоит, даже если в конечном итоге они не слишком много используют функциональные языки.
источник
sin(PI/3)
в вашем коде, где PI - константа, компилятор может оценить эту функцию во время компиляции и встроить результат в сгенерированный код.Без состояния очень легко автоматически распараллелить ваш код (поскольку процессоры создаются с все большим количеством ядер, это очень важно).
источник
Веб-приложения без сохранения состояния необходимы, когда вы начинаете иметь более высокий трафик.
Может быть много пользовательских данных, которые вы не хотите хранить на стороне клиента, например, по соображениям безопасности. В этом случае вам нужно сохранить его на стороне сервера. Вы можете использовать сеанс веб-приложений по умолчанию, но если у вас есть несколько экземпляров приложения, вам нужно будет убедиться, что каждый пользователь всегда направлен на один и тот же экземпляр.
Балансировщики нагрузки часто имеют возможность проводить «липкие сеансы», когда балансировщик нагрузки каким-то образом знает, на какой сервер отправлять запросы пользователей. Это не идеально, например, это означает, что каждый раз, когда вы перезапускаете свое веб-приложение, все подключенные пользователи теряют сеанс.
Лучший подход - хранить сеанс за веб-серверами в каком-то хранилище данных, в наши дни для этого доступно множество отличных продуктов nosql (redis, mongo, elasticsearch, memcached). Таким образом, веб-серверы не имеют состояния, но у вас все еще есть состояние на стороне сервера, и доступностью этого состояния можно управлять, выбрав правильную настройку хранилища данных. Эти хранилища данных обычно имеют большую избыточность, поэтому почти всегда должна быть возможность вносить изменения в ваше веб-приложение и даже хранилище данных, не затрагивая пользователей.
источник
Некоторое время назад я написал пост именно на эту тему: « О важности чистоты» .
источник