openlang

pure в D разрешает функции писать через аргументы

pure в D слабее, чем обещает слово, и заметно это стало только потому, что эксперимент с вычислителем захотел кэшировать вызовы.

int[] log;

pure void bump(int[] a)
{
    a[0]++;          // fine, writes through the argument
    // log ~= a[0];  // rejected, touches a mutable global
}

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

Так что если вычислитель кэширует результаты pure-функций хоста, ключ по одному атрибуту закэширует и bump, и второй вызов пропустит запись. Брать ключ по сильно чистым сигнатурам? Или список условий становится таким длинным, что проще явный атрибут на стороне хоста, которым функцию включают в кэш?

Разговор продолжается 4

Следуя доводам
  1. Ссылка на ответ

    Замените параметр на const(int)[] и посмотрите, стал ли кэш хоть чуть безопаснее. bump перестаёт компилироваться, хорошо. Но функцию с const(int)[] всё равно нельзя кэшировать, потому что const говорит лишь, что эта функция не пишет. Вызывающий код может держать изменяемую ссылку на тот же массив, поменять элемент между двумя вызовами, и закэшированный результат устареет. Что никто не пишет, обещает только immutable. Так что правило для вычислителя касается квалификатора параметра, а не отсутствия записей внутри функции, и хотелось бы видеть это прописанным везде, где хост регистрирует функцию.

  2. Ссылка на ответ

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

  3. Ссылка на ответ

    В C устроено наоборот, и это может помочь сформулировать выбор. C23 добавил атрибуты типов функций [[reproducible]] и [[unsequenced]], две степени похожего обещания. Компилятор их не проверяет. Если помеченная так функция ведёт себя не так, как обещано, поведение не определено. D проверяет pure и оставляет решение о кэше тому, кто читает сигнатуру. C позволяет оптимизатору опираться на обещание и не проверяет ничего. Для кэша вычислителя безопаснее копировать подход D, при условии, что ключом служит проверенная сигнатура, а не отдельный флаг, который хост ставит как хочет.

  4. Ссылка на ответ

    Честно, не знаю, что DMD с этим делает, и в любом случае строить что-то на том, что оптимизатор делает по случайности, не хочется. Так что да, кэш будет первым, что реально зависит от этой классификации. Тест, который регистрирует функцию хоста, меняет один параметр с immutable на const и ждёт, что регистрация откажет, написать несложно. Забавное следствие из слов nullbyte. Если ключом служит проверенная сигнатура, моему явному атрибуту делать нечего, и мне его не жаль.

Другие стороны того же вопроса

  1. Фигурные скобки вокруг expr решают, что видит компилятор байткода

    expr без фигурных скобок - единственная привычка в Tcl, которую хочется запретить! Выглядит как придирка к стилю, а на деле н…

    Ответы 13tcl
  2. Макрос не может сделать перевод строки концом формы

    Макрос не может сделать так, чтобы перевод строки закрывал форму. А хочется. Запись совсем маленькая. paint red blue на отдел…

    Ответы 4racketlispex
  3. Как воспроизвести правило, которое читает часы?

    Попытка переиграть решение о цене не удалась. Правило eligible(item), написанное на Лиспексе, читало часы хоста и ту ревизию…