Как стать автором
Обновить

Комментарии 21

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

Надо придумать какой-нибудь другой пример, где бы побочный эффект был действительно важен.
Ваша точка зрения не совпадает с точкой зрения автора, логи — очень даже наблюдаемы.
Но тот, кто наблюдает логи, хочет понять, чем именно была занята программа в тот момент времени, к которому строчка лога относится — поэтому кэшировать строки лога попросту некорректно.
Конечно некорректно, поэтому это и называется побочным эффектом, откуда следует нечистота функции.
Если вы не заметили, ваша реализация закеширует строки лога вместе со значением, если применить мемоизацию к описанным у вас функциям.
Конечно, так и задумано. Но мемоизация в данном случае будет работать корректно: при одинаковых аргументах, будет одинаковый закешированный результат. Вся хитрость в композиции функций, именно она собирает лог.
Если вы имеете ввиду, что при получении закешированного результата, писать в лог вызов функции неверно, то я с вами не соглашусь: семантически хит кеша и вызов реального кода эквивалентны.
Когда я вижу в логе строчку «запрос информации с сервера» — я хочу быть уверен, что информация и правда запрашивается, а не используется повторно ранее полученная. Если кэшировать строки лога рядом с значениями, такой уверенности у меня не будет.
Нет никакой разницы, кеширование чистых функций не меняет семантику.
Вот только в данном случае надо, чтобы она изменилась.
Кто? Строка лога? Как ее изменить? Функция чистая, она обязана вернуть одно и тоже, независимо от мемоизации. Определение такое. В статье решена задача логирования работы композиции чистых функций. Если вам нужно возвращать разные вещи, то у вас побочные эффекты, и это совсем другая задача.
Функция возвращает некоторое значение — и это значение и правда не должно меняться. Побочный эффект (запись в лог) остальной программе не виден, а потому, с точки зрения остальной программы, функция обладает всеми свойствами чистой функции.

Побочный эффект виден пользователю программы — только вот пользователю совсем не нужна неизменность лога! Увидев, что в лог больше не пишутся повторяющиеся строчки, я не буду кричать, что поймал баг. А вместо этого обрадуюсь и скажу: «смотрите — повторяющихся строчек в логе больше нет. Значит, кеширование работает!».
В данном случае лог вполне виден программе. Еще раз, решена вполне конкретная задача. Просто, не та, что вы представили в голове.
Вы можете мысленно заменить лог на шахматную доску, а функцию на совершение хода. Один и тот же ход на одинаковых досках должен делать одинаковый побочный эффект — изменение состояния доски. Так вам понятней?
Я именно об этом и говорил в первом же комментарии. Статье нужен другой пример.
Статья, как Вы могли заметить, переводная, и поменять пример не получится (хотя можно сделать комментарий от переводчика).
Ну и под логом можно понимать разные вещи.
Например данный лог поможет понять, при вызове каких функций было получено значение.
Замечание на счёт сервера некорректно, поскольку запрос с сервера (и другие операции ввода/вывод которые важно видеть в логе) — не могут быть получены из чистых функций, так что похоже в контексте статьи дизайн и смысл лога вполне корректны, хотя и отличаются от привычного нам.
Замечание на счёт сервера некорректно, поскольку запрос с сервера (и другие операции ввода/вывод которые важно видеть в логе) — не могут быть получены из чистых функций
Зависит от того, подразумевается ли вообще в задаче возможность изменения этих данных в течении времени работы программы. Если на сервере хранится какой-нибудь большой справочник — то обращение к нему через «как бы чистые» функции — оправдано.

В любом случае, смысл моего комментария не поменяется, если запрос к серверу заменить на вычисление 1000000!.
В хаскеле (*) нет понятия «данный момент времени» для функций, так как нет грязных функций, все функции существуют вне времени в идеальном мире, и всегда возвращают одно и то же при одних и тех же параметрах. Даже print это не print, это «верни действие print», которое выполнится когда-нибудь потом, вне языка, так сказать.
>Надо придумать какой-нибудь другой пример, где бы побочный эффект был действительно важен.
Другой пример трудно будет написать на С++, если даже в таком минимальном виде приходится делать хаки из С++14.

* Точнее, есть хак с unsafePerformIO, который используют для грязной отладки, но в нем-то как раз могут быть чудеса в виде выполнения программы задом наперед, например — никакой гарантии, в какой момент времени выполнится код под unsafePerformIO и выполнится ли нет. В С++ гарантии есть, поэтому «данный момент времени» обретает смысл.
Насчет времени не соглашусь. В хаскелле есть гарантии последовательности действий. Хаскелл транслируется в Core, а там есть строгие точки вычисления нормальной формы, что позволяет говорить, что вот тот код требует вот этого вычисленного значения. Так что, как таковой данный момент можно определить, как последнюю точку вычисления нормальной формы. Конечно, тогда время нелинейно, ведь иногда несколько точек одинаково хороши, но если говорить об IO, то там есть строгая последовательность.
Core, насколько я знаю, особенность реализации GHC, в стандарте есть фраза «The order of evaluation of expressions in Haskell is constrained only by data dependencies». А отсюда уже следует в том числе и гарантированная последовательность в IO: данные цепляются друг за друга так, что IO будет выполняться последовательно. Мне надо было написать 'В С++ и Haskell IO гарантии есть, поэтому «данный момент времени» обретает смысл.', но если брать IO, тогда потеряет смысл чистая монада Writer, а автор явно руководствовался двумя вещами — максимально простой и реализуемый на С++ пример, и чисто функциональный при этом.
А что касается IO, то есть еще и Lazy IO (interact, getContents, hGetContents, readFile...), где уже нет гарантий, поскольку используется unsafeInterleaveIO, что дает непредсказуемые пути развития.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории