在 Racket class 中使用 `private`(和 `define/private`)形式

Use of the `private` (and `define/private`) form in a Racket class

Racket 的 classs have a private form that can be used to hide methods. This seems to mimic use of private in other languages like Java or C#. The docs 展示了一个使用 private 隐藏方法的例子:

(define light%
  (class object%
    (super-new)
    (define on? #t)
    (define (toggle) (set! on? (not on?)))
    (private toggle)
    (define (flick) (toggle))
    (public flick)))

> (send (new light%) toggle)
send: no such method
  method name: toggle
  class name: light%
> (send (new light%) flick)

但是,默认方法似乎是私有的,无论是否实际使用了 private 关键字。例如,修改文档中的示例会产生相同的结果:

(define light%
  (class object%
    (super-new)
    (define on? #t)
    (define (toggle) (set! on? (not on?)))
    (define (flick) (toggle))
    (public flick)))

> (send (new light%) toggle)
send: no such method
  method name: toggle
  class name: light%
> (send (new light%) flick)

在这两种情况下,class 之外的范围无法访问 toggle,但它们可以访问 flick,因为它被标记为 public

那么,private是干什么用的,为什么还在语言里呢?

在第二种情况下,toggle不是方法;它是一个私有字段,其值为闭包。特别是,为每个 light% 对象分配一个新的 toggle 闭包。

使用 (private toggle) 时,toggle 方法定义被转换为采用 this 的额外参数,并且字段引用通过该参数解析。像这样:

(define (toggle-impl this-obj)
  (set-light%-on?! this-obj (not (light%-on? this-obj))))

由于它不再关闭 this,因此可以与 class 同时分配一次过程。 class 宏将 toggle 绑定到一个宏,该宏重写对 toggle 的所有调用以传递隐式 this 参数。 (这就是方法名称不是值的原因;如果你想 map 一个列表上的方法,你必须 eta-expand 所以方法名称在运算符位置。)