Функция или метод являются чистыми , если:
- у него нет побочных эффектов
- возвращаемое значение зависит только от предоставленных аргументов
Нас больше всего заинтересует первое свойство, то есть |/отсутствие побочных эффектов . Примером побочного эффекта является все, что изменяет “состояние мира”, например, изменение глобальной переменной или выполнение любого вида ввода-вывода.
Чистые функции широко описаны, см., например, статью в Википедии , сообщение в блоге с использованием Java или страницу Haskell docs .
Чистота не обязательно должна быть двоичным свойством , через. Есть некоторые побочные эффекты, о которых мы заботимся больше, чем о других. Например, ведение журнала не часто считается побочным эффектом (даже если он выполняет ввод-вывод), и, следовательно, функция, которая выполняет некоторые вычисления и выполняет ведение журнала, может по-прежнему считаться чистой.
Структурированный параллелизм является свойством параллельных программ, поскольку (в соответствии с определением Мартина Сустрика ) время жизни параллельных функций является чисто вложенным. То есть, если функция foo
запускает выполнение функции bar
в фоновом режиме ( bar
выполняется одновременно с foo
), затем bar
должен завершиться до завершения foo
.
Это гарантирует, что области действия функций, выполняемых одновременно, являются чисто вложенными. Это может показаться разумным свойством, однако на практике параллельный код редко пишется в таком стиле. Вычисления в виде потоков , волокна , сопрограммы или процессы — в зависимости от используемого языка и фреймворка — запускаются в фоновом режиме и остаются запущенными без контроля или подключения.
Натаниэль Дж. Смит проводит успешное сравнение между goto
и go
(что здесь означает неограниченное разветвление параллельных вычислений) в своем блоге ” Заметки о структурированном параллелизме, или: Заявление Go считается вредным “. В статье предлагается альтернативное введение в структурированный параллелизм; избегая goto
мы удостоверяемся, что можем читать и пытаться понять исходный код без произвольных переходов. Аналогично, избегая go
, мы можем избежать еще худшего способа перехода: в несколько местоположений одновременно , что снова облегчает чтение кода.
Другой способ взглянуть на свойство структурированного параллелизма заключается в том, что время жизни одновременно выполняемых функций должно соответствовать синтаксической структуре программы. Отсюда и название: область действия concurrency следует структуре кода.
Связаны ли два свойства, описанные выше? Да! Запуск потока и оставление его запущенным после завершения функции является побочным эффектом . Мы не только меняем состояние мира, создавая новый поток, сам поток должен выполнять некоторые действия по изменению состояния, чтобы выполнять какую-либо значимую работу (если бы все, что он делал, это запускал чистую функцию, вообще не имело бы смысла ее запускать). Это все при условии, что время жизни потока превышает время жизни вызова функции.
Что указывает на структурированный параллелизм, так это то, что этого не может произойти: побочные эффекты, связанные с потоком , не разрешены. Любые потоки, которые запускаются функцией, должны завершиться до завершения функции. Другими словами:
Функция, удовлетворяющая свойству structured concurrency , является функцией, которая является чисто потоковыми побочными эффектами wrt .
Таким образом, многопоточность становится деталью реализации функции. Функция несет ответственность за принятие решения о том, должны ли быть запущены потоки (либо для повышения производительности, либо для реализации своей логики), а также за обеспечение того, чтобы эти потоки завершались (ожидая их завершения или прерывая их).
Функция pure может по-прежнему использовать внутреннюю логику побочных эффектов, например, использовать изменяемое состояние для повышения производительности, но она не может изменять ничего глобального. Аналогично, функция threading-pure может создавать внутренние параллельные потоки выполнения, но их нельзя оставлять запущенными после завершения функции.
Другие побочные эффекты могут возникать в потоковой функции; она не обязательно должна быть чистой для всех видов побочных эффектов.
Что делать, если рассматриваемая функция возвращает Future
или Обещание
: то есть значение, представляющее вычисление, выполняемое в фоновом режиме, которое в конечном итоге даст результат? Могут ли такие функции быть структурно параллельными?
Они могут быть, но нам нужно немного скорректировать наше определение; требуя, чтобы функция, возвращающая a Future
не оставляет никаких параллельных потоков, запущенных после его завершения, что противоречит цели использования Будущее
s в первую очередь.
Следовательно, функция Future
-returningбудет удовлетворять свойству структурного параллелизма до тех пор, пока возвращаемое future завершится только после того, как все другие futures/promises , созданные функцией, также будут завершены (успешно или с ошибкой).
Опять же, это приводит к тому, что ни один поток выполнения не остается запущенным в фоновом режиме, но на этот раз, когда вычисление, возвращаемое функцией, завершается. Таким же образом мы можем расширить определение чистоты потоков.
Как насчет функциональных эффектов, то есть различных форм |/IO s? В отличие от
Future s, такие значения являются
описаниями вычислений (а не выполняемых вычислений), которые могут иметь побочные эффекты после интерпретации. То есть IO s ленивы, в то время как
Будущие ы нетерпеливы.
С помощью расширения, аналогичного тому, которое мы делали ранее, функция, возвращающая IO
, структурно параллельна, если вычисление, которое описывается значением, не оставляет никаких потоков, запущенных в качестве побочного эффекта (после его завершения). Это делает описанное вычисление “самодостаточным”.
Структурированный параллелизм – многообещающая концепция, которая предоставляет отличный инструмент для написания правильных параллельных программ и последующего их чтения. Он реализуется на Java (см. project Loom ), Котлин , Scala , Python , C и другие языки .
Чистые функции являются основным строительным блоком |/Функционального программирования (FP). Вот откуда FP черпает свою силу; код, который использует чистые функции, легче читать, отслеживать и понимать.
Структурированный параллелизм , рассматриваемый как//специализация чистоты функций в области многопоточности, является еще одним примером этого свойства. Легче понять и написать правильный код, в котором потоки не могут превышать время жизни функции, в которой они запущены.
Что может заставить вас задуматься: может быть, то же самое относится и к другим побочным эффектам, таким как ввод-вывод или глобальное состояние? Это то, что сообщество FP говорит уже давно, и в зависимости от сложности рассматриваемой проблемы, действительно, избежание таких побочных эффектов в функциях может быть полезным для удобства обслуживания и долгосрочного развития кодовой базы.
Оригинал: “https://dev.to/softwaremill/structured-concurrency-and-pure-functions-26nh”