文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
5.4 在 Scheme 中嵌入类
本节我们使用宏在 Scheme 中嵌入类。
5.4.1 类的宏
我们来定义 CLASS
语法抽象,它负责创建类:
(defmac (CLASS ([field f init] ...)
([method m params body] ...))
#:keywords field method
#:captures self
(let ([methods (list (cons 'm (λ (self)
(λ params body))) ...)])
(letrec
([class
(λ (msg . vals)
(case msg
[(create)
(make-obj class
(make-hash (list (cons 'f init) ...)))]
[(read)
(dict-ref (obj-values (first vals)) (second vals))]
[(write)
(dict-set! (obj-values (first vals)) (second vals) (third vals))]
[(invoke)
(if (assoc (second vals) methods)
(apply ((cdr (assoc (second vals) methods)) (first vals)) (cddr vals))
(error "message not understood"))]))])
class)))
5.4.2 辅助语法
我们需要引入新的语法定义,以方便地调用方法( ->
),还需要引入类似的语法,来访问当前对象的字段( ?
和 !
)。
(defmac (-> o m arg ...)
(let ((obj o))
((obj-class obj) 'invoke obj 'm arg ...)))
(defmac (? fd) #:captures self
((obj-class self) 'read self 'fd))
(defmac (! fd v) #:captures self
((obj-class self) 'write self 'fd v))
还可以定义辅助函数来创建新的实例:
(define (new c)
(c 'create))
这个简单的函数在概念上非常重要:它有助于隐藏类在内部作为函数实现的事实,还隐藏了用于请求类创建实例的符号。
5.4.3 例子
来看类的例子:
(define Point
(CLASS ([field x 0])
([method x? () (? x)]
[method x! (new-x) (! x new-x)]
[method move (n) (-> self x! (+ (-> self x?) n))])))
(define p1 (new Point))
(define p2 (new Point))
> (-> p1 move 10)
> (-> p1 x?)
10
> (-> p2 x?)
0
5.4.4 强封装
关于字段访问,我们做了个重要的设计决定:字段访问器 ?
和 !
只能作用于 self
!即,在我们的语言中不可能访问另一个对象的字段。这被称为具有 强封装 (Strong Encapsulation)对象的语言。Smalltalk 就是这样(访问另一个对象的字段实际上是发送消息,因此可以由接收方对象来控制)。Java 不是:可以访问任何对象的字段(如果可见性(visibility) 允许的话)。我们的 语法 根本不允许访问外部字段。
这样设计的另一个结果是,字段访问只能出现在方法体 内 :因为接收对象总是 self
,所以 self
必须已定义。比如说,试试在对象之外用 ?
读取字段:
> (? f)
self: undefined;
cannot reference undefined identifier
更好的做法是,上述程序会产生错误,表明 ?
未定义。要做到这一点,我们简单地将 ?
和 ?
定义为 局部 语法形式,只在方法体的内被定义,而不是全局范围内有定义。只要将这些字段访问形式的定义从全局移动到 local
作用域内, local
放在方法定义内:
(defmac (CLASS ([field f init] ...)
([method m params body] ...))
#:keywords field method
#:captures self ? !
(let ([methods
(local [(defmac (? fd) #:captures self
((obj-class self) 'read self 'fd))
(defmac (! fd v) #:captures self
((obj-class self) 'write self 'fd v))]
(list (cons 'm (λ (self)
(λ params body))) ...))])
(letrec
([class (λ (msg . vals) ....)]))))
在方法列表定义的局部作用域内定义语法形式 ?
和 !
,确保了它们可以在方法体内可用,但在其他地方不可用。
现在,字段访问器方法之外没有定义:
> (? f)
?: undefined;
cannot reference undefined identifier
后文统一使用这种局部的方法。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论