Racket's class
s 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 show an example of using private to hide a method:
(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)
However, by default methods seem to be private, whether or not there actually is a private
keyword used. For example, modifying the example from the docs yields the same results:
(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)
In both cases, scopes outside of the class don't have access to toggle
, but they do for flick
, because its marked as public
.
So then, what is private
used for, and why is it still in the language?
In the second case, toggle
is not a method; it is a private field whose value is a closure. In particular, a new toggle
closure is allocated for every light%
object.
With (private toggle)
, the toggle
method definition is transformed to take an extra argument for this
, and field references are resolved through that argument. Something like this:
(define (toggle-impl this-obj)
(set-light%-on?! this-obj (not (light%-on? this-obj))))
Since it no longer closes over this
, the procedure can be allocated once, at the same time as the class. The class
macro binds toggle
to a macro that rewrites all calls to toggle
to pass the implicit this
argument. (This is why method names are not values; if you want to map
a method over a list, you must eta-expand so the method name is in operator position.)