Имитация первоклассной библиотеки в схеме R7RS

Я думаю о реализации подобной Дилану объектной системы для Схемы. (Желательно для полностью переносимой схемы R7RS.) В Dylan существует концепция запечатанных классов: нельзя наследовать от запечатанного класса вне модуля, в котором этот класс определен.

Кажется естественным рассматривать библиотеки R7RS как модули. Однако библиотеки в R7RS Scheme являются статическими: во время выполнения ничего о них не сохраняется. После того, как привязка импортирована из библиотеки, она кажется неотличимой от всех других привязок.

Ну, это проблема для sealed реализация. Предположим, что класс создан некоторыми define-class форма. Эта форма эффективно расширяется в нечто вроде

(define <new-class> (make <class> ...))

Тогда <new-class> привязка может быть экспортирована из библиотеки, в которой она была создана, и импортирована в другую библиотеку (возможно, под другим именем). Допустим, мы создаем запечатанный <new-class> в библиотеке A и импортировать его в библиотеку B. Как можно make Вызванный из B сказать, может ли он создавать потомков <new-class> или нет? И как можно make вызывается из A разрешено создавать подклассы <new-class> безоговорочно?

(Давайте проигнорируем недостаток такого подхода: R7RS позволяет загружать <new-class> библиотека несколько раз, что эффективно создает несколько различных <new-class> Объекты класса. Я действительно не знаю, как решить эту проблему.)


Одна идея заключалась в том, чтобы заключить все определения классов в форму:

(define-library (A)
  (import (dylan))
  (export <new-class>)
  (begin
    (dylan-module
      (define-class <new-class> <object>
        ...  ) ) ) )

Запечатанные классы, определенные внутри dylan-module могут быть унаследованы от, но они действительно запечатаны, как только форма закончена. Тем не менее, я придумал только один способ реализовать это:

(define-syntax dylan-module
  (syntax-rules ()
    ((dylan-module %define-class body1 body2 ...)
     (begin
       ;; We will gather here all classes that are defined
       ;; inside the dylan-module form.
       (define to-be-sealed (list))

       ;; Locally redefine define-class to define a class
       ;; and add it to the list.
       ;;
       ;; It is necessary to pass %define-class explicitly
       ;; due to hygienic renaming: we want to allow %define-class
       ;; to be used inside of the body of the dylan-module form,
       ;; so we need to use a name from the environment where the
       ;; body is actually written.
       (let-syntax ((%define-class
                      (syntax-rules ()
                        ((%define-class name other (... ...))
                         (begin
                           (define-class name other (... ...))
                           (set! to-be-sealed
                                 (cons name to-be-sealed) ) ) ) ) ))
         body1 body2 ... )

       ;; The `seal` function is defined elsewhere.
       ;; `make` is allowed to subclass the sealed classes
       ;; until they are actually sealed by `seal`.
       (for-each seal to-be-sealed) ) ) ) )

И это используется так:

(define-library (A)
  (import (scheme base)
          (dylan) )
  (export <new-class>)
  (begin
    (dylan-module define-class
      (define-class <new-class> <object>
        ...  ) ) ) )

Тупые вещи об этом:

  • пользователь обязан разобрать define-class правильно переопределить его (в Dylan общие функции могут быть также запечатаны, так что define-generic придет после этого);

  • общий make не может создать закрытый класс безопасным способом, define-class макрос (или другой особый случай) всегда должен использоваться.

1 ответ

На мой взгляд, вы не должны пытаться переназначить библиотеки R6RS/R7RS как классы, а создавать собственные классы непосредственно в Scheme. Библиотеки предназначены для обеспечения управления пространством имен во время компиляции, а не для выполнения каких-либо действий во время выполнения.

Другие вопросы по тегам