Функциональное программирование включает в себя множество различных методов. Некоторые методы хороши с побочными эффектами. Но одним важным аспектом является логическое обоснование : если я вызываю функцию с одним и тем же значением, я всегда получаю один и тот же результат. Поэтому я могу заменить вызов функции возвращаемым значением и получить эквивалентное поведение. Это облегчает рассуждения о программе, особенно при отладке.
Если у функции есть побочные эффекты, это не совсем верно. Возвращаемое значение не эквивалентно вызову функции, поскольку возвращаемое значение не содержит побочных эффектов.
Решением является прекращение использования побочных эффектов и кодирование этих эффектов в возвращаемом значении . Разные языки имеют разные системы эффектов. Например, Haskell использует монады для кодирования определенных эффектов, таких как IO или State mutation. Языки C / C ++ / Rust имеют систему типов, которая может запретить изменение некоторых значений.
На императивном языке print("foo")
функция печатает что-либо и ничего не возвращает. В чистом функциональном языке, таком как Haskell, print
функция также принимает объект, представляющий состояние внешнего мира, и возвращает новый объект, представляющий состояние, после выполнения этого вывода. Нечто подобное newState = print "foo" oldState
. Я могу создать столько новых состояний из старого состояния, сколько захочу. Однако только одна из них будет использоваться основной функцией. Поэтому мне нужно упорядочить состояния из нескольких действий путем объединения функций. Для печати foo bar
я мог бы сказать что-то вроде print "bar" (print "foo" originalState)
.
Если состояние вывода не используется, Haskell не выполняет действия, ведущие к этому состоянию, потому что это ленивый язык. И наоборот, эта лень возможна только потому, что все эффекты явно закодированы как возвращаемые значения.
Обратите внимание, что Haskell - единственный широко используемый функциональный язык, который использует этот маршрут. Другие функциональные языки вкл. Семейство Lisp, семейство ML и более новые функциональные языки, такие как Scala, не поощряют, но допускают побочные эффекты - их можно назвать императивно-функциональными языками.
Использование побочных эффектов для ввода / вывода, вероятно, хорошо. Часто ввод / вывод (кроме регистрации) выполняется только на внешней границе вашей системы. В вашей бизнес-логике не происходит внешних коммуникаций. Тогда можно написать ядро вашего программного обеспечения в чистом стиле, все еще выполняя нечистый ввод-вывод во внешней оболочке. Это также означает, что ядро может не иметь состояния.
Безгражданство имеет ряд практических преимуществ, таких как повышенная разумность и масштабируемость. Это очень популярно для бэкэндов веб-приложений. Любое состояние хранится снаружи, в общей базе данных. Это облегчает балансировку нагрузки: мне не нужно привязывать сессии к определенному серверу. Что если мне нужно больше серверов? Просто добавьте другой, потому что он использует ту же базу данных. Что делать, если один сервер выходит из строя? Я могу повторить любые отложенные запросы на другом сервере. Конечно, еще есть состояние - в базе данных. Но я сделал это явным и извлек его, и мог бы использовать чисто функциональный подход внутри, если захочу.