#lisp #common-lisp #eval
#lisp #common-lisp #оценка
Вопрос:
В common-lisp я хочу реализовать своего рода справочную систему, подобную этой:
Предположим, что у меня есть:
(defclass reference () ((host) (port) (file)))
а также у меня есть:
(defun fetch-remote-value (reference) ...)
который извлекает и десериализует объект lisp.
Как я мог бы вмешаться в процесс оценки, чтобы всякий раз, когда оценивается ссылочный объект, извлекалось удаленное значение и повторно оценивалось снова для получения конечного результата?
Редактировать:
Более подробное описание того, чего я хочу достичь:
Используя cl-store, я сериализуюобъекты lisp и отправляю их в удаленный файл (или db или что-либо еще) для сохранения. После успешного сохранения я сохраняю хост, порт и файл в ссылочном объекте. Я хотел бы, всякий раз, когда eval вызывается для ссылочного объекта, сначала извлекать объект, а затем вызывать eval для полученного значения. Поскольку ссылка также может быть сериализована в других (родительских) объектах или агрегированных типах, я могу получить бесплатное рекурсивное разрешение удаленных ссылок, модифицируя eval, поэтому мне не нужно самостоятельно просматривать и разрешать дочерние ссылки загруженного объекта.
РЕДАКТИРОВАТЬ: Поскольку объекты всегда оценивают сами себя, мой вопрос немного неправильно задан. По сути, то, что я хотел бы сделать, это:
Я хотел бы перехватить вычисление символов, чтобы, когда их значением является объект типа REFERENCE, вместо того, чтобы возвращать объект как результат вычисления символа, возвращать результат (объект с удаленным значением)?
Ответ №1:
Короче говоря: вы не можете этого сделать, кроме как переписав функцию eval и модифицировав компилятор вашего Lisp. Правила вычисления являются фиксированным стандартом Lisp.
Редактировать После прочтения расширенного вопроса я не думаю, что вы можете добиться полной прозрачности для ваших ссылок здесь. В сценарии, подобном
(defclass foo () (reference :accessor ref))
(ref some-foo)
Результатом вызова ref является просто значение; оно не будет учитываться при вычислении независимо от его типа.
Конечно, вы могли бы определить свои средства доступа таким образом, чтобы разрешение было прозрачным:
(defmacro defresolver (name class slot)
`(defmethod ,name ((inst ,class))
(fetch-remote-reference (slot-value inst ',slot))))
(defresolver foo-reference foo reference)
Редактировать Вы можете (вроде как) подключиться к механизму разрешения символов Common Lisp с помощью символьных макросов:
(defmacro let-with-resolution (bindings amp;body body)
`(symbol-macrolet ,(mapcar #'(lambda (form) (list (car form) `(fetch-aux ,(cadr form)))) bindings) ,@body))
(defmethod fetch-aux ((any t)) any)
(defmethod fetch-aux ((any reference)) (fetch-remote-reference any))
Однако теперь все становится довольно запутанным; и переменные больше не являются переменными, а магическими символами, которые просто выглядят как переменные. Например, изменение содержимого переменной, «связанной» этим макроком, невозможно. Лучшее, что вы можете сделать с помощью этого подхода, — это предоставить setf
расширение для fetch-aux
, которое изменяет исходное место.
Комментарии:
1. Можете ли вы немного подробнее рассказать о решениях для простого закрытия?
2. Я добавил более подробное описание проблемы к вопросу
3. «Результат вызова ref — это просто значение; оно не будет учитываться при вычислении независимо от его типа» : Конечно, но если бы мы могли поместить это в процесс eval, то, если кто-то привязывает это значение к var или неявно вызывает его, оно будет автоматически извлечено и затем разрешено вычислителем. В любом случае, ваши предложения очень полезны, но я надеялся найти способ внедрить специальную обработку ссылочного типа в vains оценки.
Ответ №2:
Хотя библиотеки для отложенной оценки и сохранения объекта частично помогают вам, Common Lisp не предоставляет переносимого способа реализации полностью прозрачных постоянных значений. Отложенные или постоянные значения по-прежнему должны быть явно принудительно введены.
MOP можно использовать для реализации отложенных или постоянных объектов, хотя значения слотов прозрачно принудительно вводятся. Потребовалось бы изменить внутренние компоненты реализаций Common Lisp для обеспечения общей прозрачности, так что вы могли бы сделать, например, ( p 5)
с p
потенциально содержащим постоянное или отложенное значение.
Комментарии:
1. Я хотел бы взглянуть на это более абстрактно: в ( p 5) p может быть ссылкой на что-то произвольное, так что его значением может быть что угодно, начиная от значения в адресе памяти или ресурса, извлеченного схемой. Значение Lazy или eager ортогонально тому, где находится значение ссылки или выражения. Ленивый или нетерпеливый определяет, когда извлекать значение выражения или ссылки. Я хочу иметь возможность контролировать, ОТКУДА будет извлекаться значение выражения, либо каким-то образом специализируя вычисление выражений по ссылочному типу, либо перехватывая их результат.
2. Я знаю, что язык не предоставляет возможности полностью прозрачных постоянных значений. Я просто хотел знать: 1. Насколько сложно было бы изменить реализацию, чтобы она поддерживала полностью прозрачное и несвязанное сохранение значений. 2. Стоило бы? Я имею в виду, выиграет ли какая-либо польза от такой вещи до такой степени, которая оправдала бы попытку?
Ответ №3:
Невозможно напрямую изменить механизмы оценки. Вам нужно было бы написать компилятор для вашего кода на что-то другое. Что-то вроде встроенного языка.
На уровне CLOS есть несколько способов справиться с этим:
Два примера:
-
напишите функции, которые отправляют на ссылочный объект:
(defmethod move (позиция ссылки на объект)) (переместить позицию ссылки на разыменование))
(defmethod move ((автомобиль объекта) позиция) …))
Это становится уродливым и может быть автоматизировано с помощью макроса.
Объекты CLOS уже имеют косвенное обращение, потому что они могут изменять свой класс. Даже если они могут изменить свой класс, они сохраняют свою идентичность. CHANGE-CLASS деструктивно изменяет экземпляр.
Таким образом, это позволило бы передавать ссылочные объекты и в какой-то момент загружать данные, изменять ссылочный объект на какой-то другой класс и соответствующим образом устанавливать слоты. Это изменение класса должно быть запущено где-то в коде.
Одним из способов его автоматического запуска может быть обработчик ошибок, который улавливает некоторые виды ошибок, связанных со ссылочным объектом.
Ответ №4:
Я бы добавил слой поверх вашего механизма десериализации, который отправляет на основе типа входящих данных.