Подтвердить что ты не робот

Являются ли побочные эффекты тем, что невозможно найти в чистой функции?

Можно ли сказать, что имеет место следующая дихотомия:

Каждая заданная функция

  • либо чистый
  • или имеет побочные эффекты

Если это так, побочные эффекты (функции) - это все, что невозможно найти в чистой функции.

4b9b3361

Ответ 1

Это очень зависит от определений, которые вы выбираете. Определенно справедливо сказать, что функция чиста или нечиста. Чистая функция всегда возвращает тот же результат и не изменяет среду. Нечистая функция может возвращать разные результаты, когда она выполняется повторно (что может быть вызвано тем, что что-то делает для среды).

Являются ли все примеси побочными эффектами? Я бы так не сказал - функция может зависеть от чего-то в среде, в которой она выполняется. Это может быть чтение некоторой конфигурации, местоположения GPS или чтения данных из Интернета. Это не "побочные эффекты", потому что они ничего не делают с миром.

Я думаю, что есть два разных вида примесей:

  • Выходная примесь - это когда функция делает что-то в мире. В Haskell это моделируется с использованием monads - нечистая функция a -> b на самом деле является функцией a -> M b, где M захватывает другие вещи, которые она делает с миром.

  • Входная примесь - это когда функция требует что-то из среды. Нечистая функция a -> b может быть смоделирована как функция C a -> b, где тип C захватывает другие объекты из среды, к которой может обращаться функция.

Монад и выходные примеси, безусловно, более известны, но я думаю, что входные примеси одинаково важны. Я написал мою кандидатскую диссертацию о входных примесях, которую я называю коэффициентами, поэтому я мог бы быть предвзятым ответом.

Ответ 2

Чтобы функция была чистой, она должна:

  • не будут затронуты побочными эффектами (т.е. всегда возвращать одинаковое значение для тех же аргументов)
  • не вызывает побочных эффектов

Но, вы видите, это определяет функциональную чистоту со свойством или отсутствием побочных эффектов. Вы пытаетесь применить обратную логику, чтобы вычесть определение побочных эффектов с использованием чистых функций, которые логически должны работать, но практически определение побочного эффекта не имеет ничего общего с функциональной чистотой.

Ответ 3

Я не вижу проблем с определением чистой функции: чистая функция - это функция. То есть он имеет домен, кодомен и отображает элементы первого в элементы последнего. Он определяется на всех входах. Он ничего не делает для среды, потому что "среда" в этой точке не существует: нет машин, которые могут выполнять (для некоторого определения "выполнить" ) заданную функцию. Существует всего лишь тотальное отображение от чего-то к чему-то.

Затем какой-то капиталист решает вторгнуться в мир четко определенных функций и поработить таких чистых существ, но их справедливая сущность не может выжить в нашей жестокой реальности, функции становятся грязными и начинают нагревать процессор.

Таким образом, среда отвечает за то, что процессор нагревается, и имеет смысл говорить о чистоте до того, как ее владелец подвергся насилию и казни. Точно так же ссылочная прозрачность является свойством языка - она ​​не выполняется в среде вообще, потому что в компиляторе может быть ошибка, или метеорит может упасть на вашу голову, и ваша программа перестанет выдавать тот же результат.

Но есть и другие существа: темные обитатели подземного мира. Они выглядят как функции, но они знают об окружающей среде и могут взаимодействовать с ней: читать переменные, отправлять сообщения и запускать ракеты. Мы называем этих падших родственников функциями "нечистыми" или "эффективными" и избегаем как можно больше, потому что их природа настолько темна, что невозможно рассуждать о них.

Таким образом, существует явная разница между теми функциями, которые могут взаимодействовать с внешней стороной, и теми, которые этого не делают. Однако определение "снаружи" также может меняться. Монада State моделируется с использованием только чистых инструментов, но мы думаем о f : Int -> State Int Int как о эффективном вычислении. Более того, non-terminal и исключения (error "...") являются эффектами, но haskellers обычно не считают их такими.

Подводя итог, чистая функция представляет собой четко определенную математическую концепцию, но мы обычно рассматриваем функции в языках программирования, а то, что чистое, зависит от вашей точки зрения, поэтому не имеет смысла говорить о дихотомиях, когда они участвуют понятия не являются четко определенными.

Ответ 4

Способ определения чистоты функции f равен ∀x∀y x = y ⇒ f x = f y, т.е. с учетом того же аргумента функция возвращает тот же результат или сохраняет равенство.

Это не то, что люди обычно имеют в виду, когда говорят о "чистых функциях"; они обычно означают "чистый", поскольку "не имеют побочных эффектов". Я не понял, как квалифицировать "побочный эффект" (комментарии приветствуются!), Поэтому мне не о чем говорить.

Тем не менее, я исследую эту концепцию чистоты, потому что она может предложить некоторую взаимосвязанную проницательность. Я не эксперт здесь; это в основном я просто бессвязно. Однако я надеюсь, что это искромет некоторые проницательные (и корректирующие!) Комментарии.

Чтобы понять чистоту, мы должны знать, о каком равенстве мы говорим. Что означает x = y, а что означает f x = f y?

Один выбор - это семантическое равенство Хаскелла. То есть, равенство семантики Haskell присваивает своим членам. Насколько я знаю, для Haskell нет официальной денотационной семантики, но Wikibooks Haskell Denotational Semantics предлагает разумный стандарт, который, я думаю, более или менее соглашается на ad hoc. Когда Haskell говорит, что его функции чисты, это равенство, на которое оно ссылается.

Другой выбор - это определяемое пользователем равенство (т.е. (==)) путем получения класса Eq. Это имеет значение при использовании денотационного дизайна - то есть мы назначаем нашу семантику условиям. С помощью этого выбора мы можем случайно записать функции, которые являются нечистыми; Haskell не занимается нашей семантикой.

Я буду ссылаться на семантическое равенство Haskell как = и определяемое пользователем равенство как ==. Также я предполагаю, что == является отношением равенства - это не выполняется для некоторых экземпляров ==, например, для Float.

Когда я использую x == y в качестве предложения, что я действительно имею в виду, это x == y = True ∨ x == y = ⊥, потому что x == y :: Bool и ⊥ :: Bool. Другими словами, когда я говорю, что x == y истинно, я имею в виду, что если он вычисляет что-то другое, кроме ⊥, то он вычисляет значение True.

Если x и y равны в соответствии с семантикой Хаскелла, то они равны по любой другой семантике, которую мы можем выбрать.

Доказательство: если x = y, то x == y ≡ x == x и x == x истинно, потому что == является чистым (согласно =) и рефлексивным.

Аналогично доказывается ∀f∀x∀y x = y ⇒ f x == f y. Если x = y, то f x = f y (потому что f чисто), поэтому f x == f y ≡ f x == f x и f x == f x истинно, потому что == является чистым и рефлексивным.

Вот глупый пример того, как мы можем нарушить чистоту для пользовательского равенства.

data Pair a = Pair a a

instance (Eq a) => Eq (Pair a) where
  Pair x _ == Pair y _ = x == y

swap :: Pair a -> Pair a
swap (Pair x y) = Pair y x

Теперь мы имеем:

Pair 0 1 == Pair 0 2

Но:

swap (Pair 0 1) /= swap (Pair 0 2)

Примечание: ¬(Pair 0 1 = Pair 0 2), поэтому нам не гарантировано, что наше определение (==) будет в порядке.

Более убедительным примером является рассмотрение Data.Set. Если x, y, z :: Set A, то вы надеетесь, что это будет выполнено, например:

x == y ⇒ (Set.union z) x == (Set.union z) y

Особенно, когда Set.fromList [1,2,3] и Set.fromList [3,2,1] обозначают один и тот же набор, но, вероятно, имеют разные (скрытые) представления (не эквивалентные семантике Haskell). То есть мы хотим быть уверены, что ∀z Set.union z является чистым в соответствии с (==) для Set.

Вот тип, с которым я играл:

newtype Spry a = Spry [a]

instance (Eq a) => Eq (Spry a) where
  Spry xs == Spry ys = fmap head (group xs) == fmap head (group ys)

A Spry - это список, который имеет не равные смежные элементы. Примеры:

Spry [] == Spry []
Spry [1,1] == Spry [1]
Spry [1,2,2,2,1,1,2] == Spry [1,2,1,2]

Учитывая это, что представляет собой чистая реализация (согласно == для Spry) для flatten :: Spry (Spry a) -> Spry a такая, что если x является элементом суб-spry, это также элемент сплющенного spry (то есть что-то как ∀x∀xs∀i x ∈ xs[i] ⇒ x ∈ flatten xs)? Упражнение для читателя.

Также стоит отметить, что функции, о которых мы говорили, находятся в одном домене, поэтому они имеют тип A → A. То есть, за исключением тех случаев, когда мы доказали ∀f∀x∀y x = y ⇒ f x == f y, который пересекает семантический домен Haskell в наш собственный. Это может быть какой-то гомоморфизм... возможно, теоретик категории может весить здесь (и, пожалуйста, сделайте!).

Ответ 5

Побочные эффекты являются частью определения языка. В выражении

f e

побочные эффекты e - это все части поведения e, которые "вытесняются" и становятся частью поведения выражения приложения, а не передаются в f как часть значения e.

Для конкретного примера рассмотрим эту программу:

f x = x; x
f (print 3)

где концептуально синтаксис x; x означает "запустите x, затем запустите его снова и верните результат".

В языке, где print записывает в stdout как побочный эффект, это записывает

3

потому что вывод является частью семантики выражения приложения.

В языке, где вывод print не является побочным эффектом, он пишет

3
3

потому что вывод является частью семантики переменной x в определении f.