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

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

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

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

Ответ 1

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

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

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

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

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

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

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

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

Ответ 4

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

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

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

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

Ответ 5

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

Ответ 6

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

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

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

Ответ 8

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

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

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

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