Вроде
реализовал иммутабельность для хеш-таблиц. Цитата из тестов:
(defparameter *hash-table-to-freeze* (make-hash-table))(with-test (:name :table-is-freezed-and-its-type-is-changed)
(assert (typep *hash-table-to-freeze* '(and hash-table (mutable 1))))
(freeze-object *hash-table-to-freeze*)
(assert (typep *hash-table-to-freeze* '(and hash-table (mutable 0)))))(with-test (:name :writing-to-mutable-and-immutable-hash-table)
(defun mutate-the-mutable-hash-table (x)
(setf (gethash 33 (the (and hash-table (mutable 1)) x)) 55))
(compile 'mutate-the-mutable-hash-table)
(assert (= 55 (mutate-the-mutable-hash-table (make-hash-table))))
(assert-error (mutate-the-mutable-hash-table *hash-table-to-freeze*)))(with-test (:name :compiler-warns-on-writing-to-hash-table-declared-as-immutable)
(macrolet ((compilation-failure-p (definition)
`(multiple-value-bind (function warnings-p failure-p)
(let ((*evaluator-mode* :interpret))
(eval '(compile ,definition)))
failure-p)))
(assert (compilation-failure-p
(defun mutate-the-hash-table-declared-immutable (x)
(declare (type (and hash-table (mutable 0)) x))
(setf (gethash 4 x) 5))))
(assert (compilation-failure-p
(defun mutate-the-hash-table-declared-mutable (x)
(declare (type (mutable 0) x))
(setf (gethash 4 x) 5))))))Если попросту, то декларация типа или (the (mutable 1) x) говорит о том, что данный объект - мутабельный (но реализовано это только для хеш-таблиц).
Соответственно (the (mutable 0) x) говорит о том, что x - иммутабельный. Хеш-таблица всегда рождается мутабельной, но может быть заморожена вызовом freeze-object, после чего становится иммутабельной. Компиляция кода, пишущего в объект, декларированный иммутабельным, приводит к предупреждению компиляции. Попытка выполнить такой код приводит к ошибке времени выполнения. Попытка записи в замороженный объект является ошибкой (соответствующая проверка стоит в и рантайме).
Есть некие намётки, чтобы поддерживать иммутабельность структур. Во-первых, структура иммутабельна, если все её поля - read-only. Но этого недостаточно. Нужно ещё уметь замораживать структуру. Пока не умею это сделать, и не факт, что успею за оставшееся отведённое время.
Ещё хочется, но пока страшно попробовать интегрировать иммутабельность в систему вывода типов. В SBCL есть два механизма вывода типов - собственно вывод типов и "constraint propagation", который задаёт локальные ограничения, например, для веток А и Б в (if (typep x 'foo) А Б). Я не знаю, понимают ли эти механизмы время. Т.е. можно ли им объяснить, что если объект сейчас немутабельный, то в будущем он останется немутабельным, а в прошлом может быть и мутабельным. Так что пока я вообще исключил мутабельность из вывода типов - она будет работать только с declare type и с явным the.
Что немаловажно, можно декларировать (and (mutable 0) hash-table) и вроде это нормально обрабатывается.