Я в основном программист на C / C ++, а это значит, что большая часть моего опыта связана с процедурными и объектно-ориентированными парадигмами. Однако, как известно многим программистам на C ++, C ++ с годами сместил акцент на стиль функционального esque, завершившийся, наконец, добавлением лямбд и закрытий в C ++ 0x.
Несмотря на это, хотя у меня есть значительный опыт написания кода в функциональном стиле с использованием C ++, у меня очень мало опыта работы с реальными функциональными языками, такими как Lisp, Haskell и т. Д.
Недавно я начал изучать эти языки, потому что идея «без побочных эффектов» в чисто функциональных языках всегда интересовала меня, особенно в том, что касается приложений для параллелизма и распределенных вычислений.
Однако, исходя из опыта C ++, я не понимаю, как эта философия «без побочных эффектов» работает с асинхронным программированием. Под асинхронным программированием я подразумеваю любой фреймворк / API / стиль кодирования, который отправляет предоставляемые пользователем обработчики событий для обработки событий, которые происходят асинхронно (вне потока программы). Это включает в себя асинхронные библиотеки, такие как Boost.ASIO, или даже просто старый C обработчики сигналов или обработчики событий Java GUI.
Единственное, что у них общего, - то, что природа асинхронного программирования, по-видимому, требует создания побочных эффектов (состояния), чтобы основной поток программы осознавал, что обработчик асинхронного события был вызван. Как правило, в среде, подобной Boost.ASIO, обработчик событий изменяет состояние объекта, так что эффект события распространяется за пределы срока службы функции обработчика событий. Действительно, что еще может сделать обработчик событий? Он не может «вернуть» значение точке вызова, потому что нет точки вызова. Обработчик событий не является частью основного потока программы, поэтому единственный способ, которым он может оказать какое-либо влияние на реальную программу, - это изменить какое-то состояние (или другое состояние longjmp
на другую точку выполнения).
Таким образом, кажется, что асинхронное программирование - это асинхронное создание побочных эффектов. Это кажется полностью противоречащим целям функционального программирования. Как эти две парадигмы согласованы (на практике) в функциональных языках?
источник
Ответы:
Вся ваша логика здрава, за исключением того, что я думаю, что ваше понимание функционального программирования слишком экстремально. В реальном мире функциональное программирование, как объектно-ориентированное или императивное программирование, связано с мышлением и подходом к проблеме. Вы все еще можете писать программы в духе функционального программирования, изменяя состояние приложения.
На самом деле, вы должны изменить состояние приложения, чтобы сделать что-нибудь. Ребята из Haskell скажут вам, что их программы «чистые», потому что они обертывают все свои изменения состояния в монаду. Однако их программы все еще взаимодействуют с внешним миром. (Иначе какой смысл!)
Функциональное программирование подчеркивает «отсутствие побочных эффектов», когда это имеет смысл. Однако, чтобы заниматься программированием в реальном мире, как вы сказали, вам нужно изменить состояние мира. (Например, реагирование на события, запись на диск и т. Д.)
Для получения дополнительной информации об асинхронном программировании на функциональных языках я настоятельно призываю вас изучить модель программирования F # Asynchronous Workflows . Это позволяет вам писать функциональные программы, скрывая все грязные детали перехода потоков внутри библиотеки. (В манере, очень похожей на монады в стиле Haskell.)
Если «тело» потока просто вычисляет значение, то создание нескольких потоков и параллельное вычисление их значений все еще входит в функциональную парадигму.
источник
Это увлекательный вопрос. На мой взгляд, наиболее интересным является подход, принятый в Clojure и объясненный в этом видео:
http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey
В основном предлагаемое «решение» заключается в следующем:
Вероятно, я не выразил эту идею так ясно, как это сделали другие, но я надеюсь, что это дает общую идею - в основном она использует параллельную систему STM для обеспечения «моста» между чисто функциональным программированием и асинхронной обработкой событий.
источник
Одно замечание: функциональный язык чистый, но его среда выполнения - нет.
Например, среды выполнения Haskell включают очереди, мультиплексирование потоков, сборку мусора и т. Д., Причем все это не чисто.
Хороший пример - лень. Haskell поддерживает ленивую оценку (это по умолчанию, на самом деле). Вы создаете ленивое значение, подготавливая операцию, затем вы можете создать несколько копий этого значения, и оно все еще «ленивое», если оно не требуется. Когда нужен результат или если время выполнения находит какое-то время, значение фактически вычисляется, и состояние ленивого объекта меняется на то, что больше не требуется выполнять вычисления (еще раз), чтобы получить его результат. Теперь он доступен по всем ссылкам, поэтому состояние объекта изменилось, хотя это чистый язык.
источник
Это было бы смысл тогда.
Стиль звука без побочных эффектов несовместим с фреймворками, которые зависят от состояния. Найти новую основу.
Например, стандарт Python WSGI позволяет нам создавать приложения без побочных эффектов.
Идея состоит в том, что различные «изменения состояния» отражаются средой значений, которые могут быть построены постепенно. Каждый запрос представляет собой конвейер преобразований.
источник
Изучив инкапсуляцию в Borland C ++ после изучения C, когда в Borland C ++ не было шаблонов, позволяющих использовать дженерики, парадигма ориентации на объекты сделала меня неловким. Несколько более естественный способ вычисления - фильтрация данных по каналам. Выходящий поток имел отдельную и независимую идентичность с внутренним неизменным входным потоком, а не был побочным эффектом, т. Е. Каждый источник данных (или фильтр) был автономен от других. Нажатие клавиши (пример события) ограничивало асинхронные комбинации пользовательского ввода доступными кодами клавиш. Функции работают с аргументами входных параметров, а состояние, инкапсулированное классом, является просто ярлыком для избежания явной передачи повторяющихся аргументов между небольшим подмножеством функций, а также для предосторожности в отношении связанного контекста, предотвращающего злоупотребление этими аргументами из любой произвольной функции.
Строгое следование определенной парадигме вызывает неудобство, связанное с утечкой абстракций, например. коммерческие среды выполнения, такие как JRE, DirectX, .net целевые объектно-ориентированные сторонники в первую очередь. Чтобы ограничить неудобства, языки либо выбирают сложные в научном отношении монады, такие как Haskell, либо гибкую поддержку нескольких парадигм, например, F #. Если инкапсуляция не используется в некоторых случаях использования множественного наследования, многопарадигмальный подход может быть лучшей альтернативой некоторым, иногда сложным, специфичным для парадигмы шаблонам программирования.
источник