У меня уже не раз задавался этот вопрос.
Общий вопрос
Можно ли прозрачно локально затенять функцию f
с помощью оболочки с тем же именем f
?
То есть, как локально (f wrapped-args ...) расширяться до (f args ...)?
Кажется, что Flet позволяет нам это делать, но имеет ограничения, а именно, получившуюся оболочку нельзя установить. Можно ли сделать это, не прибегая к флету?
В идеале был бы макрос, который позволяет нам писать обернутые f
вызовы и расширяет код до исходного не обернутого f
вызова.
Сначала я полагал, что макролет может быть таким, поскольку в документации говорится, что он сначала расширяет макрос, а затем применяет setf к развернутой форме, но я не могу его использовать (продолжайте читать ниже).
Мотивация
Это полезно в контекстах, где некоторые параметры являются неявными и не должны повторяться снова и снова, для большего количества СУХОГО кода.
В моем предыдущем вопросе (let-curry) есть конкретный пример что. Попытка автоматически назначить некоторые параметры функций (let-curry).
Предостережения флета
Я получил там отличные ответы, однако я столкнулся с некоторыми ограничениями. При обращении к flet для выполнения такого локального затенения имени функции в оболочке над ней такие оболочки не могут быть установлены, поэтому такие оболочки не могут использоваться так же гибко, как исходная функция, только для чтения значений, а не для записи.
Конкретный вопрос
По приведенной выше ссылке, как можно написать макрос flet-curry и настроить функции оболочки?
Бонус: может ли этот макрос расширить обернутые вызовы до исходных с нулевыми издержками времени выполнения?
Я попытался взять выбранный ответ в этом посте и использовать макролет вместо флета, но безрезультатно.
Спасибо!
ОБНОВИТЬ
Меня попросили привести конкретный пример для этого общего вопроса.
Комментарии желаний в коде:
(locally (declare (optimize safety))
(defclass scanner ()
((source
:initarg :source
:accessor source
:type string)
(tokens
:initform nil
:accessor tokens
:type list)
(start
:initform 0
:accessor start
:type integer)
(current
:initform 0
:accessor current
:type integer)
(line
:initform 1
:accessor line
:type integer))
(:metaclass checked-class)))
(defun lox-string (scanner)
"Parse string into a token and add it to tokens"
;; Any function / defmethod / accessor can be passed to let-curry
;; 1. I'd like to add the accessor `line` to this list of curried methods:
(let-curry scanner (peek at-end-p advance source start current)
(loop while (and (char/= #\" (peek))
(not (at-end-p)))
do
;; 2. but cannot due to the incf call which calls setf:
(if (char= #\Newline (peek)) (incf (line scanner))
(advance)))
(when (at-end-p)
(lox.error::lox-error (line scanner) "Unterminated string.")
(return-from lox-string nil))
(advance) ;; consume closing \"
(add-token scanner 'STRING (subseq (source)
(1+ (start))
(1- (current))))))
Это означает, что я бы хотел let-curry
преобразовать любой вызов каррированных функций в этом блоке из
(f arg1 arg2 ...)
to(f scanner arg1 arg2 ...)
на месте, как если бы я написал вторую форму, а не первую в исходном коде. Если бы это было в случае с каким-то «макросом», то он был бы настроен по дизайну.
Кажется, макрос был бы правильным инструментом для этого, но я не знаю, как это сделать.
Спасибо еще раз :)
PS: Если вам нужен доступ к полному коду, он находится здесь: https://github.com/AlbertoEAF/cl-lox (scanner.lisp)
setf
это? У вас есть пример безflet-curry
? - person Sylwester   schedule 24.05.2020setf
able - см. Мой ответ ниже. - person   schedule 26.05.2020