Является ли функция немедленно нечистой, если она принимает функцию в качестве параметра?

17

Так как чистота входного параметра неизвестна до времени выполнения, считается ли функция немедленно нечистой, если она принимает функцию в качестве входного параметра?

Связано: если функция применяет чистую функцию, которая определена вне функции, но не передана в качестве параметра, будет ли она все еще чистой, если она удовлетворяет критериям отсутствия побочных эффектов, а результат зависит исключительно от ввода?

Для контекста я пишу функциональный код на JavaScript.

Dancrumb
источник
В качестве тривиального встречного примера рассмотрим:foo = function(function bar){ print(bar.toString()) }
Дэвид говорит восстановить Монику
1
@DavidGrinberg Я думаю, что это не контр-пример, и на самом деле выдвигает на первый план большую проблему; если у вас есть функции, которые можно переопределить, и вы не можете гарантировать, что реализации не имеют побочных эффектов, то вы не можете гарантировать, что большинство функций, которые принимают объекты и вызывают их методы, также являются чистыми. Может быть, toString () бара удаляет некоторые файлы с диска?
Джошуа Тейлор
3
@DavidGrinberg Но, я думаю, вы думаете в правильном направлении. foo = function(function bar) { return 3; } является чистым, и принимает функцию в качестве аргумента.
Джошуа Тейлор
@JoshuaTaylor Справедливо, я об этом не думал. Но вы уже по существу решили проблему. В качестве альтернативного исправления просто вызовите «корень» toString()(то есть тот, который вы найдете в объекте Java).
Дэвид говорит восстановить Монику

Ответы:

22

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

Фасет, который выводится одинаково каждый раз для одного и того же ввода, определяется тем, являются ли параметры чистыми. Если вы предполагаете, что параметры (например, аргумент функции) также являются чистыми, то это чисто.

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

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

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

Daenyth
источник
Что касается вашего утверждения, что «пока все значения, используемые в функции, определяются исключительно ее параметрами, это чистая функция». Что происходит в случае констант? Если у меня есть функция areaOfCircle r => Math.Pi * r * r, будет ли она areaOfCircleне чистой, поскольку она не просто использует параметры?
Дэвид Арно
2
@DavidArno Это справедливо. Согласно ссылочной прозрачности, ссылка на статическое внешнее значение ничем не отличается от жестко закодированного, поэтому она все равно будет чистой.
Дейнит
1
«это означает, что чистая функция может иметь нечистое поведение» - по определению, чистая функция не может иметь нечистое поведение. Вы делаете ошибку, думая, что функция, f(f2)которая вызывает f2, не полагается на то, на что f2полагается. Функция, которая может вызывать произвольные переданные функции, не является чистой.
user2357112 поддерживает Монику
2
@Daenyth: лучше, но все равно предполагается, что функция должна вызывать переданную функцию. Это может быть что-то вроде function compose(f, g) {return function h(x) {return f(g(x));};}, что чисто, несмотря на то, что функции принимают в качестве аргументов.
user2357112 поддерживает Monica
1
«Я нахожу редким использование нечистых функций в качестве аргументов функций для чистых функций». - не функциональный язык, но некоторые функции библиотеки C ++ имеют определенные предупреждения о том, что аргументы предиката должны быть (в некотором приближении) чистыми. Таким образом, в некотором смысле это означает, что это не только редко, но и никогда не происходит. Но в другом смысле факт, что они должны это запретить, заключается в том, что люди иногда хотят это сделать. Например, они хотят передать findподпрограмме нечистый предикат, который возвращает «true» для третьего соответствующего элемента, с которым она сталкивается, или какую-то подобную чепуху.
Стив Джессоп
19

Так как чистота входного параметра неизвестна до времени выполнения, считается ли функция немедленно нечистой, если она принимает функцию в качестве входного параметра?

Контрпример:

function pure(other_function) {
    return 1;
}

Неважно, other_functionявляется ли это чистая функция, нечистая функция или не функция вообще. pureФункция является чистой.

Другой контрпример:

function identity(x) {
    return x;
}

Эта функция чиста, даже если xэто нечистая функция. identity(impure_function)всегда будет возвращаться impure_function, независимо от того, сколько раз вы повторите вызов. Не имеет значения, identity(impure_function)()всегда ли возвращает одно и то же; функция , в возвращаемом значении возвращаемого значения в не влияет на его чистоту.


В общем, если функция может вызвать функцию, переданную в качестве аргумента, она не является чистой. Например, функция function call(f) {f();}не является чистой, потому что, хотя она не упоминает ни о каком глобальном или изменяемом состоянии, она fможет вызывать alertвидимые побочные эффекты.

Если функция принимает функции в качестве аргументов, но не вызывает их или не вызывает их, то она может быть чистой. Это может все еще быть нечистым, если он делает какую-то другую нечистую вещь. Например, function f(ignored_function) {alert('This isn't pure.');}нечист, даже если он никогда не звонит ignored_function.

user2357112 поддерживает Monica
источник
4
Этот ответ кажется чрезмерно педантичным. Из вопроса можно сделать вывод, что проблема заключается в вызываемом параметре функции. Существование функций, которые могут / действительно принимают другие функции в качестве параметра, не вызывая их, не влияет на этот вопрос.
Walpen
13
@walpen: в вопросе не упоминается аргументация. Нет никаких оснований предполагать, что спрашивающий даже осознал, что функция может принимать другую функцию в качестве входных данных без ее вызова. Важно указывать на подобные скрытые предположения, а не просто предполагать, что вы должны были их принять.
user2357112 поддерживает Monica
12

Так как чистота входного параметра неизвестна до времени выполнения, считается ли функция немедленно нечистой, если она принимает функцию в качестве входного параметра?

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

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

Да. Итак, давайте сосредоточимся на том, что здесь важно. Вызов функции чистый или нет, сам по себе бесполезен. Чистые функции полезны, потому что создание одного и того же вывода для любого ввода, не зависящего от состояния или наличия побочных эффектов, является очень полезным набором свойств. Это означает, что после запуска вашей функции вы можете «запомнить» ответ для этого ввода, и он всегда будет верным. Вам также не нужно снова запускать функцию, чтобы генерировать побочные эффекты.И вы можете запустить эту функцию параллельно (или не по порядку) с другими функциями и знать, что у них не будет никаких скрытых взаимодействий, которые ведут себя плохо.

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

Telastyn
источник
5

Как сказал Теластин: Технически, да, если только в вашем языке нет способа гарантировать, что функция ввода также чиста.

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

Такая чистая функция, которую вы написали бы в JavaScript как

function foo(f) {
   return f(1) + 2;
}

можно перевести непосредственно на Haskell:

foo :: (Int -> Int) -> Int
foo f = f 1 + 2

Теперь в JavaScript вы можете делать такие злые вещи, как

js> foo (function(x) {console.log("muharhar"); return 0})
muharhar
2

Это невозможно в Хаскеле . Причина в том, что что-то вроде побочного эффекта console.log()всегда должно иметь тип результата IO something, а не только somethingодин.

GHCi> foo (\x -> print "muarhar" >> return 0)

<interactive>:7:12:
    Couldn't match expected type ‘Int’ with actual type ‘IO b0’
    In the expression: print "muarhar" >> return 0
    In the first argument of ‘foo’, namely
      ‘(\ x -> print "muarhar" >> return 0)’
    In the expression: foo (\ x -> print "muarhar" >> return 0)

Для этого выражения для проверки типов нам нужно дать fooсигнатуру типа

foo :: (Int -> IO Int) -> Int

Но оказывается, что я больше не могу это реализовать: поскольку функция аргумента имеет IOсвой результат, я не могу использовать его внутри foo.

<interactive>:8:44:
    Couldn't match expected type ‘Int’ with actual type ‘IO Int’
    In the first argument of ‘(+)’, namely ‘f 1’
    In the expression: f 1 + 2

Единственный способ использовать IOдействие в fooтом случае, если результат fooимеет IO Intсам тип :

foo :: (Int -> IO Int) -> IO Int
foo f = do
   f1 <- f 1
   return (f1 + 2)

Но на данный момент из подписи ясно, fooчто это не чистая функция.

leftaroundabout
источник
1
Прежде чем сказать «невозможно», взгляните на unsafeIO:-)
Bergi
2
@Bergi: на самом деле это не часть Haskell, а его интерфейс с внешней функцией: чтобы можно было утверждать, что функция, определенная в каком-то другом языке, является чистой, что компилятор Haskell, очевидно, не может вывести из сигнатуры типа, потому что другие языки обычно не имеют такая вещь как IO. Кстати, его также можно использовать, чтобы вызвать хаос, скрывая побочные эффекты в «чистой» функции, но это действительно небезопасно в Haskell, поскольку на самом деле нет надежного способа указать порядок оценки чистых функций.
оставил около
Да, это правда. Тем не менее, я думаю, что я видел, как он использовался, чтобы «скрыть» полезные побочные эффекты и в «чистой» функции, где безопасность подхода должна была быть установлена ​​ранее.
Берги
@Bergi Вы в основном не должны использовать unsafeIO; это последний спасательный люк, который обходит гарантии системы типов, и, следовательно, ваш не очень хороший момент.
Андрес Ф.
0

Нет это не так.

Если переданная функция является нечистой И ваша функция вызывает переданную функцию, то ваша функция будет считаться нечистой.

Отношение чистый / нечистый немного похоже на синхронизацию / асинхронность в JS. Вы можете свободно использовать чистый код из нечистых, но не наоборот.

Бобби Маринов
источник
Этот ответ не добавляет ничего, что еще не было объяснено в этом ... Пожалуйста, просмотрите предыдущие ответы, прежде чем что-то повторять :)
Andres F.
Как насчет синхронизации / асинхронной аналогии?
Бобби Маринов