LISP-условный ответ без пакета

Трудно найти подходящее название, потому что я не понимаю, почему у меня возникает следующая проблема.

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

(cond ((find-package 'foo) (setf *special-function* #'foo:foo-function))
      ((find-package 'bar) (setf *special-function* #'bar:bar-function))
      (t (warn "The package FOO or BAR is required to enable the special function.")))

Затем оценка этого фрагмента кода возвращает:

There is no package named "FOO" с БКЛ

Package FOO does not exist с SBCL

Основная причина, по которой я хочу это сделать, заключается в том, что существуют разные пакеты, обеспечивающие одну и ту же функцию, и я хочу, чтобы пользователь мог сделать свой собственный выбор, загрузив пакет FOO или BAR (с предпочтением в соответствии с порядком в пункты условий).


person yannics    schedule 18.09.2019    source источник


Ответы (2)


Подумайте о выполнении/оценке следующей формы:

(if (find-package 'foo)
    (foo:foo-function))

Лисп

  1. читает код
  2. а затем оценивает код.

Ваша ошибка возникает на этапе 1: Чтение. Форма не может быть прочитана, когда пакет не существует.

Типичный способ обойти это будет следующим:

(if (find-package 'foo)
    (funcall (find-symbol "FOO-FUNCTION" "FOO")))

Затем мы ищем символ во время выполнения. Обратите внимание, что имена функций и пакетов по умолчанию пишутся в верхнем регистре.

В вашем случае вам нужно позвонить

(symbol-function (find-symbol "FOO-FUNCTION" "FOO"))

чтобы получить объект функции. Обратите внимание, что иногда лучше просто получить символ.

funcall и apply могут вызывать глобальные функции как функциональный объект или как символ:

(funcall 'sin 2.1d0)

or

(funcall #'sin 2.1d0)

Таким образом, когда вещь нужно будет вызвать позже, символ предлагает другой источник косвенности и будет указывать на текущее определение, где объект функции также может быть более старой версией функции.

person Rainer Joswig    schedule 18.09.2019

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

Поскольку, как писал Райнер, нам нужно различать случаи уже во время чтения, мы можем использовать для этого условия чтения. Удобно, что все реализации помещают свое имя в *features*, так что мы можем сделать это следующим образом:

(defun special-function (&rest args)
  #+sbcl (apply #'foo:foo-function args)
  #+ccl (apply #'bar:bar-function args)
  #-(or sbcl ccl) (error "not implemented))

Если вместо этого вы хотите, чтобы разные библиотеки заполнили вашу функциональность, вы можете либо положиться на то, что пользователь загрузит одну из них перед загрузкой вашей системы, если эти библиотеки помещают свои собственные символы в *features*, либо вы можете определить небольшие системы-оболочки, которые добавляют необязательная зависимость:

Пример: ваша (asdf) система называется yoursys, тогда вы можете определить другую систему yoursys/foo-backend и yoursys/bar-backend, каждая из которых установит вашу special-function в соответствующие реализации и зависит от необходимой библиотеки.

person Svante    schedule 18.09.2019