racket/collects/unstable/scribblings/class-iop.scrbl

129 lines
3.9 KiB
Racket

#lang scribble/manual
@(require scribble/eval
"utils.rkt"
(for-label unstable/class-iop
racket/class
racket/contract
racket/base))
@title[#:tag "class-iop"]{Interface-Oriented Programming for Classes}
@(define the-eval (make-base-eval))
@(the-eval '(require racket/class unstable/class-iop))
@defmodule[unstable/class-iop]
@unstable[@author+email["Ryan Culpepper" "ryanc@racket-lang.org"]]
@defform[(define-interface name-id (super-ifc-id ...) (method-id ...))]{
Defines @racket[name-id] as a static interface extending the
interfaces named by the @racket[super-ifc-id]s and containing the
methods specified by the @racket[method-id]s.
A static interface name is used by the checked method call variants
(@racket[send/i], @racket[send*/i], and @racket[send/apply/i]). When
used as an expression, a static interface name evaluates to an
interface value.
@examples[#:eval the-eval
(define-interface stack<%> () (empty? push pop))
stack<%>
(define stack%
(class* object% (stack<%>)
(define items null)
(define/public (empty?) (null? items))
(define/public (push x) (set! items (cons x items)))
(define/public (pop) (begin (car items) (set! items (cdr items))))
(super-new)))
]
}
@defform[(define-interface/dynamic name-id ifc-expr (method-id ...))]{
Defines @racket[name-id] as a static interface with dynamic
counterpart @racket[ifc-expr], which must evaluate to an interface
value. The static interface contains the methods named by the
@racket[method-id]s. A run-time error is raised if any
@racket[method-id] is not a member of the dynamic interface
@racket[ifc-expr].
Use @racket[define-interface/dynamic] to wrap interfaces from other
sources.
@examples[#:eval the-eval
(define-interface/dynamic object<%> (class->interface object%) ())
object<%>
]
}
@defform[(send/i obj-exp static-ifc-id method-id arg-expr ...)]{
Checked variant of @racket[send].
The argument @racket[static-ifc-id] must be defined as a static
interface. The method @racket[method-id] must be a member of the
static interface @racket[static-ifc-id]; otherwise a compile-time
error is raised.
The value of @racket[obj-expr] must be an instance of the interface
@racket[static-ifc-id]; otherwise, a run-time error is raised.
@examples[#:eval the-eval
(define s (new stack%))
(send/i s stack<%> push 1)
(send/i s stack<%> popp)
(send/i (new object%) stack<%> push 2)
]
}
@defform[(send*/i obj-expr static-ifc-id (method-id arg-expr ...) ...)]{
Checked variant of @racket[send*].
@examples[#:eval the-eval
(send*/i s stack<%>
(push 2)
(pop))
]
}
@defform[(send/apply/i obj-expr static-ifc-id method-id arg-expr ... list-arg-expr)]{
Checked variant of @racket[send/apply].
@examples[#:eval the-eval
(send/apply/i s stack<%> push (list 5))
]
}
@defform[(define/i id static-ifc-id expr)]{
Checks that @racket[expr] evaluates to an instance of
@racket[static-ifc-id] before binding it to @racket[id]. If
@racket[id] is subsequently changed (with @racket[set!]), the check is
performed again.
No dynamic object check is performed when calling a method (using
@racket[send/i], etc) on a name defined via @racket[define/i].
}
@deftogether[[
@defform[(init/i (id static-ifc-id maybe-default-expr) ...)]
@defform[(init-field/i (id static-ifc-id maybe-default-expr) ...)]
@defform/subs[(init-private/i (id static-ifc-id maybe-default-expr) ...)
([maybe-default-expr (code:blank)
default-expr])]]]{
Checked versions of @racket[init] and @racket[init-field]. The value
attached to each @racket[id] is checked against the given interface.
No dynamic object check is performed when calling a method (using
@racket[send/i], etc) on a name bound via one of these forms. Note
that in the case of @racket[init-field/i] this check omission is
unsound in the presence of mutation from outside the class. This
should be fixed.
}