Почему Today () является примером нечистой функции?

38

Кажется, что, читая что-то вроде этой статьи в Википедии о «чистых функциях» , они приводят Today()в качестве примера нечистую функцию, но она кажется мне довольно чистой. Это потому, что нет формального входного аргумента? Почему фактическое время дня не рассматривается как «входные данные для функции», и в этом случае, если вы дали ему один и тот же ввод, т.е. выполнили today()два раза в одно и то же время, или переместились назад во времени, чтобы выполнить его снова (возможно, гипотетически: )), на выходе будет одновременно. Today()никогда не дает вам случайное число. это всегда дает вам время суток.

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

штифтик
источник
8
Если бы вы перешли во время дня, что бы функция делала?
JB Кинг
1
Я ожидаю, что это даст вам время суток. (не самая полезная функция). Но у этого нет никакого аргумента, который я думаю, является корнем ответа.
Брэд
3
Можете ли вы предсказать его вывод (на основе предоставленных вами входных параметров)?
Даниэль Б
1
@DanielB Нет никакой предсказательной силы для отсутствующего / нулевого входного параметра, как выясняется. Единственное, что я могу сделать, это посмотреть на мои наручные часы (мой мобильный телефон).
Брэд
«Почему фактическое время дня не рассматривается как« вход в функцию »» Это, в основном, проблема, которую монады пытаются решить. Чистые функции могут основываться только на их входах и не иметь побочных эффектов. Если вы сделаете «состояние мира до меня» входным значением и «состояние мира после меня» частью возвращаемого значения и пропустите эти состояния мира через свою программу, вы снова сможете быть чистыми.
Шон МакSomething

Ответы:

103

Это потому, что нет формального входного аргумента?

Это связано с тем, что результат зависит от того, что не является входом, а именно от текущего времени.

Почему фактическое время дня не рассматривается как «вход в функцию»

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

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

Майкл Боргвардт
источник
2
Таким образом, даже несмотря на то, что время реальности является своего рода входом, потому что оно не дается как вход, и оно находится вне контроля функции (как внутри функции, так и вне контроля того, кто вызывает Today()), Today()становится нечистым. Today()Функция может быть немного глупый пример. Более подходящей может быть какая-то Count()функция. При одинаковом количестве предметов для подсчета Count()всегда будет возвращаться одно и то же число, но поскольку это выходит за рамки Count()его, это нечисто.
Брэд
1
@brad, это что-то вроде серой области - есть неявный фактический аргумент - массив или список. Учитывая неизменный список и один и тот же аргумент каждый раз, он всегда будет возвращать одно и то же значение.
Макс
34
«время реальности - это своего рода вход» - да; действительно, глобальное состояние неявно доступно (т. е. «своего рода вход») для всех функций, но если они зависят от своего результата, они нечисты!
AakashM
4
@Brad count()на большинстве языков программирования определенно чист. У него есть явное входное значение: коллекция, количество которой вы хотите. Не смущайтесь синтаксисом, таким как myCollection.count(); это просто сахар для count(myCollection).
Андрес Ф.
Отличный ответ, как всегда, но он явно не охватывает неизменяемые свободные переменные. Они не являются входными данными для функции - не передаются в качестве параметра - но функция зависит от них, даже если она по-прежнему прозрачна по ссылкам.
24

sin(x)всегда будет возвращать одно и то же значение, пока xостается неизменным. Today()может возвращать разные результаты с течением времени, потому что это зависит от значений вне вашего контроля . Например, если что- $current_datetime то, находящееся вне контроля вашей программы, изменит внутреннее состояние системы во время ее работы, оно Today()внезапно даст другие результаты.

FrustratedWithFormsDesigner
источник
«всегда будет возвращать другое значение» - это немного ... нечистая формулировка. Википедия говорит, что «возвращает текущий день недели», что означает, что значения, полученные по понедельникам, не будут отличаться
комнат
7
@gnat: Правда, если что-то внешнее для вашей программы не изменило внутренний календарь вашего компьютера, так что он вдруг подумал, что это четверг. Тогда вызов Today()будет возвращать «четверг» в понедельник.
FrustratedWithFormsDesigner
3
@gnat Ну, это не всегда будет возвращать другое значение (вряд ли есть полезная функция, которая делает). Но, как и большинство нечистых функций, возвращаемое значение может изменяться даже во время выполнения одной программы (например, если она выполняется в течение ночи).
3
@delnan: Да, это проклятие авторов наивных баз данных! : P "Но КАК он мог пропустить 300 записей? Сценарий работал нормально, когда я проверял его вчера утром!"
FrustratedWithFormsDesigner
@delnan это точно. Я только указал на то, что использование всегда в первоначальной формулировке (исправлено в текущей версии ответа может ) было несколько неточным
комнат
13

Today () - это нечистая функция, потому что ее результат зависит от того, чего вы не даете; в частности, текущее системное время. Следовательно, его результат не является детерминированным, если он основан только на входных данных, предоставленных при вызове.

Чистая функция была бы int Add(int a, int b) {return a + b;}. Функция работает исключительно с тем, что ей дано, и не использует никаких других данных внешнего состояния. Естественным результатом этого является то, что вы можете Add(2,2)и получить 4 с этого момента до конца времени. Кроме того, поскольку функция не изменяет никакое внешнее состояние (у нее нет «побочных эффектов»), Add () ing 2 и 2 с этого момента до конца времени не изменит ничего в системе, если только вы не присвойте результат функции переменной или иным образом используйте значение для обновления состояния (которое не является операцией, выполняемой самой функцией). Практически все классические математические операции являются чистыми функциями и могут быть реализованы как таковые.

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

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

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

Keiths
источник
8

Кажется довольно очевидным, что эта функция не проходит первый тест на чистоту, данный в самом начале этой страницы:

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

Обратите внимание, что поскольку он не принимает аргументов, существует только один возможный набор значений аргумента - пустой набор. И эта функция может и действительно возвращает разные результаты для одного и того же «значения аргумента».

Кроме того, значение результата функции действительно зависит от «скрытой ... состояние , которое может изменяться по мере выполнения программы продолжается». Итак, очередной провал.

AakashM
источник
@ JörgWMittag Я не уверен, где я утверждаю, что функция без аргументов не может вернуть значение.
AakashM
Мозг пердеть. Я прочитал «есть только один возможный набор возвращаемых значений».
Йорг W Mittag
8

() => 1будет чистой функцией, поскольку она всегда возвращает 1. Today()может возвращать «понедельник» или «вторник» или почти любое другое значение.

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

Однако вам не нужно знать ничего особенного о состоянии мира, чтобы знать, что это sin(x)такое. И когда-либо вызов sin(x)для данного xбудет возвращать одно и то же значение.

Guvante
источник
Википедия говорит «возвращает текущий день недели», то есть может возвращать понедельник, вторник и т. Д., Но не «23.01.2013» и не «24.01.2013»
комнат
7
@gnat: Обновлено, но разница не была существенной.
Гуванте
2

Date(timestamp)будет чистой функцией. Из-за его идемпотентности. И потому что не будет никакого побочного эффекта.

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

Флориан Маргейн
источник
2

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

newValue = Function();
while(true)
{
   oldValue = newValue;
   newValue = Function();
   assert( newValue == oldValue );
}

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

oldValue = Function( importantVariableToYourApp );
newValue = Function( importantVariableToYourApp );
assert( newValue == oldValue );

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

Дрейк Кларрис
источник
2

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

Следовательно, todayлибо не является функцией вообще, следовательно, не является чистой функцией. Или мы можем интерпретировать синтаксис

today()

немного так, что это значит

today   ()      -- today, applied to the value ()

Например, в Haskell это будет правильно:

data Day = Mon | Tue | Wed | Thu | Fri | Sat | Sun deriving Show
today :: () -> Day
today () = ....?
main = print (today())

потому что есть тип () с одним значением ().

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

Системный таймер является отличным примером для глобального состояния.

Инго
источник
1

Проблема в today()том, что он может давать другой результат, если вызывается два или более раз в функции.

Вот пример кода, который может привести к ошибке.

function doSomething(when)
{
     if(today() == when)
     {
           // open a resource or create a temp file.....
     }

     // do some other work

     if(today() == when)
     {
           // close the resource or delete temp file.....
     }
}

Это возможно в приведенном выше примере. Это второе ifутверждение не будет выполнено. Даже если первый сделал. Оставив ресурс в плохом состоянии.

Reactgular
источник
1

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

Каждый раз, когда мы вызываем Today(), мы предоставляем ему одни и те же параметры (нет), но не обязательно получаем один и тот же результат (понедельник, вторник и т. Д.).

Zantier
источник
4
это, кажется, просто повторяет высказанное и объясненное в верхнем ответе , опубликованном около двух лет назад. Вряд ли стоит натолкнуться на двухлетний вопрос с таким содержанием
gnat
1
Я не слишком знаком с тем, как работает stackexchange, но я подумал, что, поскольку это было в главных вопросах, это уже было затронуто. Что касается повторения, я помню, как читал на мета, что может быть полезно иметь несколько похожих ответов. Я чувствую, что мой лаконичен и потенциально полезен.
Зантье