Что именно означает "эффективный"
Время от времени я читаю термин "эффект", но я все еще не могу дать четкое определение того, что это значит. Я предполагаю, что правильный контекст - это эффективные вычисления, но я также видел термин эффектный значения)
Раньше я думал, что эффективные средства имеют побочные эффекты. Но в Haskell нет побочных эффектов (кроме как в некоторой степени IO). Все еще есть эффективные вычисления повсюду.
Затем я прочитал, что монады используются для создания эффективных вычислений. Я могу несколько понять это в контексте Монады State
. Но я не вижу побочного эффекта в монаде Maybe
. В общем, мне кажется, что Monads, которые обертывают функционально подобную вещь, легче видеть как производящие побочные эффекты, чем Monads, которые просто обертывают ценность.
Когда дело доходит до функторов Applicative
, я еще больше теряюсь. Я всегда видел аппликативные функторы как способ map
функции с несколькими аргументами. Здесь я не вижу побочного эффекта. Или есть разница между эффектами и эффектами?
Ответы
Ответ 1
A побочный эффект является наблюдаемым взаимодействием с его средой (помимо вычисления его значения результата). В Haskell мы стараемся избегать функций с такими побочными эффектами. Это относится даже к действиям IO
: когда выполняется действие IO
, никаких побочных эффектов не выполняется, они выполняются только тогда, когда действия, предписанные в значении IO
, выполняются в пределах main
.
Однако при работе с абстракциями, связанными с составлением вычислений, такими как аппликативные функторы и монады, удобно несколько различать фактическое значение и "остальное", которое мы часто называем "эффектом". В частности, если у нас есть тип f
kind * -> *
, то в f a
часть a
- это значение и любое другое "остается" - это "эффект" .
Я намеренно процитировал термины, поскольку нет точного определения (насколько я знаю), это просто разговорное определение. В некоторых случаях вообще нет значений или нескольких значений. Например, для Maybe
"эффект" заключается в том, что не может быть значения (и вычисление прерывается), для []
"эффект" заключается в том, что существует несколько (или нулевых) значений. Для более сложных типов это различие может быть еще сложнее.
Различие между "эффектами" и "значениями" на самом деле не зависит от абстракции. Functor
, Applicative
и Monad
просто дают нам инструменты, что мы с ними можем сделать (Functor
позволяют изменять значения внутри, Applicative
позволяют комбинировать эффекты и Monad
позволяют влиять эффекты на предыдущие значения). Но в контексте Monad
s несколько легче создать ментальную картину того, что происходит, потому что монадическое действие может "видеть" значение результата предыдущего вычисления, о чем свидетельствует
(>>=) :: m a -> (a -> m b) -> m b
Оператор : вторая функция получает значение типа a
, поэтому мы можем представить себе, что "предыдущее вычисление имело некоторый эффект, и теперь есть его значение результата, с которым мы можем что-то сделать".
Ответ 2
На мой взгляд, "побочный эффект" - это то, что нормальная функция не могла сделать. Другими словами, все, кроме просто возвращающего значение.
Рассмотрим следующий блок кода:
let
y = foo x
z = bar y
in foobar z
Это вызывает foo
, а затем вызывает bar
, а затем вызывает foobar
три обычные функции. Достаточно просто, не так ли? Теперь рассмотрим следующее:
do
y <- foo x
z <- bar y
foobar z
Это также вызывает три функции, но также невидимо вызывает (>>=)
между каждой парой строк. И это означает, что происходят какие-то странные вещи, в зависимости от того, какой тип монады выполняет функции.
-
Если это точная монада, ничего особенного не происходит. Монадическая версия делает то же самое, что и чистая версия. Нет побочных эффектов.
-
Если каждая функция возвращает Maybe
-something, то, если (скажем) bar
возвращает Nothing
, весь блок кода прерывается. Нормальная функция не может этого сделать. (I.e., в чистом варианте, нет способа предотвратить вызов foobar
.) Таким образом, эта версия делает то, что чистая версия не может. Каждая функция может вернуть значение или прервать блок. Это побочный эффект.
-
Если каждая функция возвращает список-что-то, тогда код выполняется для всех возможных комбинаций результатов. Опять же, в чистой версии нет способа заставить любую из функций выполнять несколько раз с разными аргументами. Так что побочный эффект.
-
Если каждая функция выполняется в монаде состояния, то (например) foo
может отправлять некоторые данные непосредственно в foobar
, в дополнение к значению, которое вы можете видеть, проходя через bar
. Опять же, вы не можете сделать это с чистыми функциями, так что побочный эффект.
-
В IO
монаде у вас есть всевозможные интересные эффекты. Вы можете сохранять файлы на диск (файл в основном представляет собой гигантскую глобальную переменную), вы даже можете повлиять на работу кода на других компьютерах (мы называем этот сетевой ввод-вывод).
-
Монада ST
является сокращенной версией монады IO
. Он допускает изменчивое состояние, но автономные вычисления не могут влиять друг на друга.
-
Монада STM
позволяет нескольким потокам разговаривать друг с другом и может привести к тому, что код будет выполняться несколько раз, и... ну, вы не можете сделать это с нормальными функциями.
-
Монада продолжения позволяет разбить людей! Возможно, это возможно с чистыми функциями...
Ответ 3
В подтверждение ответ Петра Пудлака, здесь приведено аргумент о происхождении более широкого понятия "эффекта" .
Фраза "эффектное программирование" проявляется в реферате Макбрайда и Паттерсона "Прикладное программирование с эффектами" , в котором были представлены аппликативные функции:
В этой статье мы вводим функторы Applicative
- абстрактную характеристику аппликативного стиля эффектного программирования, слабее чем Monad
и, следовательно, более широко распространены.
"Эффект" и "Эффективный" появляются в нескольких других отрывках из статьи; эти последствия считаются совершенно незаметными, чтобы не требовать явного разъяснения. Например, это замечание сделано сразу после представления определения Applicative
(стр. 3):
В каждом примере существует конструктор типа f
, который встраивает обычный понятие ценности, но поддерживает свой собственный особый способ придавать смысл обычному применимому языку [...] Соответственно мы вводим класс Applicative
:
[Определение Haskell Applicative
]
Этот класс обобщает S и K [т. комбинаторы S и K, которые отображаются в экземпляре Reader
/function Applicative
] для потоковой передачи среды для потоковой передачи эффекта в целом.
Из этих кавычек мы можем заключить, что в этом контексте:
-
Эффекты - это те, что Applicative
потоки "вообще.
-
Эффекты связаны с конструкторами типов, которым присваиваются экземпляры Applicative
.
-
Monad
также относится к эффектам.
Следуя этим выводам, мы можем отследить это использование "эффекта", по крайней мере, документы Wadler на монадах. Например, здесь приведена цитата со страницы 6 из Monads для функционального программирования:
В общем случае функция типа a → b заменяется функцией типа a → M b. Это можно прочитать как функцию, которая принимает аргумент типа a и возвращает результат типа b, с возможным дополнительным эффектом, захваченным M. Этот эффект может заключаться в том, чтобы воздействовать на состояние, генерировать вывод, создавать исключение или что у вас есть.
И из той же статьи, стр. 21:
Если монады инкапсулируют эффекты и списки образуют монаду, то списки соответствуют некоторому эффекту? Действительно, они делают, и эффект, которым они соответствуют, - это выбор. Можно подумать о вычислении типа [a], предлагая выбор значений, по одному для каждого элемента списка. Монадический эквивалент функции типа a → b является функцией типа a → [b].
"Соответствовать некоторому эффекту", фраза здесь ключевая. Это связано с более простым утверждением в абстрактном выражении:
Монады обеспечивают удобную структуру для имитации эффектов, найденных на других языках, таких как глобальное состояние, обработка исключений, вывод или недетерминированность.
Шаг состоит в том, что монады могут использоваться для выражения вещей, которые на "других языках" обычно кодируются как побочные эффекты, т.е. как Петр Пудлак ставит в своем ответе здесь "наблюдаемое взаимодействие с [ функциональная] среда (кроме вычисления ее значения результата)". Благодаря метонимии это легко привело к "эффекту", приобретая второе значение, более широкое, чем "побочный эффект", а именно, что бы ни вводилось конструктором типа, являющимся экземпляром Monad
. Со временем этот смысл был далее обобщен, чтобы охватить другие классы функторов, такие как Applicative
, как видно из работы Макбрайда и Паттерсона.
В заключение я считаю, что "эффект" имеет два разумных значения в языке Хаскелла:
В некоторых случаях избегаемые разногласия по терминологии случаются, когда каждая из вовлеченных сторон неявно принимает другое значение "эффекта". Другой возможный спор заключается в том, является ли законным говорить о эффектах, когда речь идет только о Functor
, в отличие от подклассов, таких как Applicative
или Monad
(я считаю, что это нормально, что согласуется с Петр Пудлак отвечает: Почему аппликативные функторы имеют побочные эффекты, но функторы не могут?).