Преимущества программирования без сохранения состояния? - PullRequest
124 голосов
/ 10 мая 2009

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

Стоит ли продолжать углубленное изучение Haskell (или другого чисто функционального языка)? Является ли функциональное программирование или программирование без сохранения состояния более продуктивным, чем процедурное? Вполне вероятно, что я продолжу использовать Haskell или другой функциональный язык позже, или я должен выучить его только для понимания?

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

Ответы [ 8 ]

159 голосов
/ 10 мая 2009

Чтение Функциональное программирование в двух словах .

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

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

42 голосов
/ 10 мая 2009

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

Вы можете найти хороший учебник с множеством примеров в статье Джона Хьюза Почему функциональное программирование имеет значение (PDF).

Вы будете гобсами более продуктивными, особенно если вы выберете функциональный язык, который также имеет алгебраические типы данных и сопоставление с образцом (Caml, SML, Haskell).

19 голосов
/ 10 мая 2009

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

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

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

Также в F # вместо написания циклов вы вызываете функции. Это небольшое изменение, но существенное, потому что вам больше не нужно думать о конструкции цикла. Например, вот фрагмент кода, который должен пройти и сопоставить что-либо (я не могу вспомнить, что, это из головоломки проекта Эйлера):

let matchingFactors =
    factors
    |> Seq.filter (fun x -> largestPalindrome % x = 0)
    |> Seq.map (fun x -> (x, largestPalindrome / x))

Я понимаю, что сделать фильтр, а затем карту (это преобразование каждого элемента) в C # было бы довольно просто, но вы должны думать на более низком уровне. В частности, вам нужно написать сам цикл и иметь собственное явное выражение if и тому подобное. После изучения F # я понял, что мне стало проще кодировать функциональным образом, где, если вы хотите фильтровать, вы пишете «filter», а если вы хотите отобразить, вы пишете «map» вместо реализации каждая из деталей.

Мне также нравится оператор |>, который, я думаю, отделяет F # от ocaml и, возможно, другие функциональные языки. Это оператор канала, он позволяет вам «передать» вывод одного выражения на вход другого выражения. Это заставляет код следовать тому, как я думаю больше. Как и в приведенном выше фрагменте кода, говорится: «возьмите последовательность факторов, отфильтруйте ее, затем сопоставьте». Это очень высокий уровень мышления, которого вы не понимаете в императивном языке программирования, потому что вы так заняты написанием цикла и операторов if. Это одна вещь, по которой я скучаю больше всего, когда перехожу на другой язык.

Так что в общем, хотя я могу программировать как на C #, так и на F #, мне проще использовать F #, потому что вы можете мыслить на более высоком уровне. Я бы сказал, что из-за того, что мелкие детали удалены из функционального программирования (по крайней мере, в F #), я более продуктивен.

Редактировать : Я видел в одном из комментариев, которые вы просили, пример "состояния" в функциональном языке программирования. F # может быть написано обязательно, поэтому вот прямой пример того, как вы можете иметь изменяемое состояние в F #:

let mutable x = 5
for i in 1..10 do
    x <- x + i
14 голосов
/ 10 мая 2009

Рассмотрим все сложные ошибки, которые вы потратили на отладку в течение долгого времени.

Теперь, сколько из этих ошибок было связано с "непреднамеренным взаимодействием" между двумя отдельными компонентами программы? (Почти все ошибки в потоке имеют такую ​​форму: гонки, связанные с записью общих данных, взаимоблокировки, ... Кроме того, обычно можно найти библиотеки, которые неожиданно влияют на глобальное состояние, или прочитать / записать реестр / среду и т. Д.) Я предположил бы, что по крайней мере 1 из 3 «жуков» попадает в эту категорию.

Теперь, если вы переключитесь на программирование без состояния / неизменяемое / чистое, все эти ошибки исчезнут. Вместо этого вы сталкиваетесь с некоторыми новыми проблемами (например, когда вы do хотите, чтобы разные модули взаимодействовали со средой), но на языке, таком как Haskell, эти взаимодействия явно преобразуются в систему типов, что означает, что вы можете просто посмотрите на тип функции и рассмотрите тип взаимодействия, которое она может иметь с остальной частью программы.

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

7 голосов
/ 10 мая 2009

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

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

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

5 голосов
/ 10 мая 2009

Без состояния очень легко автоматически распараллелить ваш код (так как процессоры сделаны с большим количеством ядер, это очень важно).

4 голосов
/ 22 августа 2013

Веб-приложения без сохранения состояния важны, когда вы начинаете увеличивать трафик.

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

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

Лучший подход - хранить сеанс за веб-серверами в каком-либо хранилище данных, в наши дни для этого доступно множество отличных продуктов nosql (redis, mongo ,asticsearch, memcached). Таким образом, веб-серверы не сохраняют состояние, но у вас все еще есть состояние на стороне сервера, и доступностью этого состояния можно управлять, выбрав правильную настройку хранилища данных. Эти хранилища данных обычно имеют большую избыточность, поэтому почти всегда можно вносить изменения в ваше веб-приложение и даже в хранилище данных, не влияя на пользователей.

4 голосов
/ 28 января 2010

Я недавно написал пост только на эту тему: О важности чистоты .

...