Имитация первоклассной библиотеки в схеме 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. Библиотеки предназначены для обеспечения управления пространством имен во время компиляции, а не для выполнения каких-либо действий во время выполнения.