Преимущества программирования без сохранения состояния?

StackOverflow https://stackoverflow.com/questions/844536

Вопрос

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

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

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

Это было полезно?

Решение

Читать Коротко о функциональном программировании.

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

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

Другие советы

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

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

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

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

На самом деле я обнаружил (из личного опыта), что программирование на 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#, я понял, что мне проще писать функциональный код: если вы хотите фильтровать, вы пишете «фильтр», а если хотите сопоставить, вы пишете «карта», а не реализацию. каждая из деталей.

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

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

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

let mutable x = 5
for i in 1..10 do
    x <- x + i

Подумайте обо всех сложных ошибках, на отладку которых вы потратили долгое время.

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

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

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

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

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

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

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

Некоторое время назад я написал пост как раз на эту тему: О важности чистоты.

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top