Что именно означает "эффективный"

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

Раньше я думал, что эффективные средства имеют побочные эффекты. Но в 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 (я считаю, что это нормально, что согласуется с Петр Пудлак отвечает: Почему аппликативные функторы имеют побочные эффекты, но функторы не могут?).