Common Lisp: Лучший способ временно импортировать несколько функций из пакета - PullRequest
5 голосов
/ 07 января 2012

Есть ли способ временно импортировать несколько функций из пакета в текущий пакет с использованием стандартных функций / макросов common-lisp?

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

(defmacro with-functions (functions the-package &body body)
  "Allows functions in the-package to be visible only for body.
  Does this by creating local lexical function bindings that redirect calls
  to functions defined in the-package"
  `(labels
     ,(mapcar (lambda (x) `(,x (&rest args)
                               (apply (find-symbol ,(format nil "~:@(~a~)" x) 
                                                   ,the-package)
                                      args)))
              functions)
     ,@body))

Пример использования:

(defclass-default test-class ()
  ((a 5 "doc" )
   (b 4 "doc")))
#<STANDARD-CLASS TEST-CLASS>
CL-USER> 
(with-functions (class-direct-slots slot-definition-name) 'sb-mop
  (with-functions (slot-definition-initform) 'sb-mop
    (slot-definition-initform
      (car (class-direct-slots (find-class 'test-class))))))
5
CL-USER> 

РЕДАКТИРОВАТЬ: Включены некоторые предложения Райнера в макрос.

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

Я пытался написать макрос с импортом, который использовал shadowing-import и unintern, но я не смог заставить его работать. У меня были проблемы с читателем, который сказал, что импортированные функции еще не существовали (во время чтения) до того, как код, который импортировал функции, был оценен.

Я думаю, что заставить его работать с shadowing-import и unintern лучше, так как это будет намного чище, быстрее (без возможности поиска во время выполнения) и работать с функциями и символами в пакетах.

Мне было бы очень интересно посмотреть, сможет ли кто-нибудь написать макрос с импортом с помощью unintern и shadowing-import.

Ответы [ 2 ]

2 голосов
/ 08 января 2012

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

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

Его наименование сбивает с толку.'import' - это пакетная операция, и пакеты работают только с символами, а не с функциями.Вы не можете импортировать функцию в пакете, только символ.

(labels ((foo () 'bar))
  (foo))

Имя лексической функции FOO только в исходном коде является символом.Позже невозможно получить доступ к функции через символ источника (например, с помощью (symbol-function 'foo)).Если компилятор скомпилирует приведенный выше код, ему не нужно хранить символ - он не нужен, кроме как для целей отладки.Ваш вызов APPLY не сможет найти какую-либо функцию, созданную LABELS или FLET.

Ваш макрос не импортирует символ, он создает привязку локальной лексической функции.

Для немного похожих макросов смотрите CL:WITH-SLOTS и CL:WITH-ACCESSORS.Они не поддерживают поиск во время выполнения, но допускают эффективную компиляцию.

Ваш макрос не выглядит так (здесь используется "CLOS" в качестве пакета, так же, как ваша "SB-MOP"):

(defpackage "P1" (:use "CL"))
(defpackage "P2" (:use "CL"))

(with-import (p1::class-direct-slots) 'CLOS
  (with-import (p2::class-direct-slots) 'P1
    (p2::class-direct-slots (find-class 'test-class))))

Сгенерированный код:

(LABELS ((P1::CLASS-DIRECT-SLOTS (&REST ARGS)
           (APPLY (FIND-SYMBOL "CLASS-DIRECT-SLOTS" 'CLOS) ARGS)))
  (LABELS ((P2::CLASS-DIRECT-SLOTS (&REST ARGS)
             (APPLY (FIND-SYMBOL "CLASS-DIRECT-SLOTS" 'P1) ARGS)))
    (P2::CLASS-DIRECT-SLOTS (FIND-CLASS 'TEST-CLASS))))
1 голос
/ 07 января 2012

Вы можете использовать import со списком квалифицированных символов (например, package:symbol или package::symbol), которые вы хотите импортировать, а затем unintern их.

...