Почему функция с бесполезным изолированным «статическим» считается нечистой? - PullRequest
4 голосов
/ 05 марта 2020

В статье Википедии о Чистая функция есть пример нечистой функции, подобной этой:

void f() {
  static int x = 0;
  ++x;
}

С замечанием "из-за мутации локального состояния c переменная ".

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

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

Итак, почему этот бесполезный изолированный static int делает функцию нечистой?

Ответы [ 2 ]

7 голосов
/ 05 марта 2020

Результат чистой функции полностью определяется ее входными аргументами. Здесь результат означает не только возвращаемое значение, но и эффект с точки зрения виртуальной машины, определенной стандартом C / C ++. Другими словами, если функция иногда демонстрирует неопределенное поведение с одинаковыми входными аргументами, ее нельзя считать чистым (поскольку поведение отличается от одного вызова к другому с одним и тем же вводом) .

В конкретном случае с локальной переменной stati c эта переменная может стать источником гонки данных, если f вызывается одновременно в нескольких потоках. Гонка данных означает неопределенное поведение. Другим возможным источником UB является целочисленное переполнение со знаком, которое может в конечном итоге произойти.

0 голосов
/ 05 марта 2020

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

не имеет видимого побочного эффекта

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

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

Кажется, что проще просто спроектировать компилятор, чтобы всегда отмечать функцию как нечистую, если она имеет static локальный, вместо того, чтобы писать логи c в нижнюю часть и указывать, запоминается ли функция или нет. Видите местный static? Boom: больше не чистый.

Итак, с точки зрения компилятора, он нечистый.

А как насчет других свойств чистой функции?

тестируемый, разумные и самодокументированные

Тесты, как правило, пишутся человеком, поэтому я бы сказал, что эта функция поддается тестированию. Хотя некоторые автоматизированные программы для написания тестов могут снова увидеть, что они не запоминаются, и просто решили полностью игнорировать написание тестов. Это гипотетическое программное обеспечение может просто пропустить что-нибудь с локальными static s. Опять же, гипотетически.

Является ли код разумным? Конечно, нет. Хотя я не уверен, насколько это важно. Это не ничего не делает . Это затрудняет понимание. («Почему Боб написал эту функцию таким образом? Это ситуация Magic / More Magi c?»).

Самодокументируется ли код? Опять я бы сказал нет. Но опять же, это пример вырожденного кода.

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

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

"В заключение, мне все равно, чисто оно или нет".

Позвольте мне перефразировать из моего предыдущего (вышеприведенного) заключения.

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

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

Как бы это ни было, ответ действительно зависит от того, что вы используете для определения pure за. Если это пишет компилятор? Вероятно, хотите использовать более консервативное определение чистого, которое допускает ложные срабатывания и исключает эту функцию. Если это должно впечатлить группу студентов-второкурсников CS, слушая Zep? Go для определения, которое признает это, не имеет побочных эффектов и назовите это днем.

...