Оценка кода, который ссылается на параметры функции - PullRequest
0 голосов
/ 01 февраля 2020

Обновление: оригинальная версия этого вопроса не полностью описывает ограничения моей ситуации. Теперь он включает в себя пример с некоторыми махающими руками для простоты, а также пример минимума, который можно проверить.

Я пытаюсь передать фрагмент кода в процедуру для оценки в более поздний момент времени. Система со всеми ограничениями выглядит следующим образом:

; External Library code - cannot be changed
(define (serialize-parameters . args)
  (format some-file "~A~%" args))

(define (function-called-later-possibly-after-a-system-reboot callback)
  (apply callback some-default-parameters (deserialize-parameters some-file)))

; Internal library code - can be changed, but the value of `code-snippet` cannot be hardcoded
(define (callback some-default-parameters code-snippet)
  (eval code-snippet (current-module))

; Application code - can be changed, can be hardcoded
(define (main)
  (serialize-parameters #:code-snippet '(format #t "~A~%" some-default-parameters)))

Суть в том, чтобы приложение могло выполнять произвольный фрагмент кода в функции обратного вызова. Функция обратного вызова принимает другие параметры, которые не известны приложению, и может изменяться между вызовами.

Важным ограничением является то, что параметры, которые приложение хочет отправить в обратный вызов, записываются в файл и затем считываются обратно. в более поздний момент времени, возможно, после перезагрузки системы.

Этот пример проще протестировать, но он не охватывает все ограничения:

(define (test foo bar)
    (eval bar (current-module)))

(test "value of foo" '(format #t "~A~%" foo))

Запуск этой программы приводит к Unbound variable: foo. Я хочу, чтобы функция test была определена таким образом, чтобы результатом вызова было «значение foo \ n», выводимое на терминал. Возможно ли это в Guile?

Спасибо.

1 Ответ

2 голосов
/ 02 февраля 2020

Это не будет работать. eval принимает окружение, и вы передаете его (current-module). Это привязки верхнего уровня в модуле, такие как test, но не лексические привязки, такие как foo или bar. Они просто не существуют в среде, возвращаемой (current-module).

Вы можете сделать это:

(define foo "value of foo")
(eval '(format #t "~A~%" foo) (current-module))
; prints "value of foo"
; ==> #t

Кроме того, слон в комнате, что вы можете сделать это с лямбдами:

(define (test value proc)
  (proc value))

(test "value to process" (lambda (foo) (format #t "~A~%" foo)))
; prints "value to process"
; ==> #t

В качестве альтернативы, но я ' Я предполагаю, что вы не можете иметь format в обратном вызове, потому что «код-фрагмент» может иметь много разных значений:

(define (main)
  (serialize-parameters #:code-snippet "~A~%"))

(define (callback some-default-parameters code-snippet)
  (format #t code-snippet some-default-parameters))

РЕДАКТИРОВАТЬ

Я думаю, вы можете сделать это наполовину жестко:

(define (main)
  (serialize-parameters #:code-snippet 'print-format))

(define (callback argument message)
  (case message
    ((print-format) (format #t "~A~%" argument))
    ((other-message) (handle-message ...))
    (else ...)))

Вы даже можете сделать это динамическим c диспетчером. Например. вы делаете что-то вроде этого:

(define messages '())
(define (register-callback message proc)
  (set! messages (cons (cons message proc) messages)))

(define (callback argument message)
  (let ((found (assq message messages)))
    (when found
      ((cdr found) argument))))

(register-callback 'print-format (lambda (arg) (format #t "~A~%" arg)))

(callback "test" 'print-format) ; prints "test"

Теперь в файле сохраняется только сообщение, которое может быть любым литералом данных.

...