122 lines
4.2 KiB
Racket
122 lines
4.2 KiB
Racket
#lang scribble/manual
|
|
@(require (for-label racket/base racket/generic))
|
|
|
|
@title[#:tag "struct-generics"]{Generic Interfaces}
|
|
@; @author[@author+email["Eli Barzilay" "eli@racket-lang.org"]
|
|
@; @author+email["Jay McCarthy" "jay@racket-lang.org"]
|
|
@; @author+email["Vincent St-Amour" "stamourv@racket-lang.org"]
|
|
@; @author+email["Asumu Takikawa" "asumu@racket-lang.org"]]
|
|
|
|
@defmodule[racket/generic]
|
|
|
|
A @deftech{generic interface} allows per-type methods to be
|
|
associated with generic functions. Generic functions are defined
|
|
using a @racket[define-generics] form. Method implementations for
|
|
a structure type are defined using the @racket[#:methods] keyword
|
|
(see @secref["define-struct"]).
|
|
|
|
@defform/subs[(define-generics name [#:defined-table defined-table]
|
|
[method . kw-formals*]
|
|
...)
|
|
([kw-formals* (arg* ...)
|
|
(arg* ...+ . rest-id)
|
|
rest-id]
|
|
[arg* id
|
|
[id]
|
|
(code:line keyword id)
|
|
(code:line keyword [id])])
|
|
#:contracts
|
|
([name identifier?]
|
|
[defined-table identifier?]
|
|
[method identifier?])]{
|
|
|
|
Defines @racket[gen:name] as a transformer binding for the static
|
|
information about a new generic interface.
|
|
|
|
Defines @racket[name?] as a predicate identifying instances of structure
|
|
types that implement this generic group.
|
|
|
|
Defines each @racket[method] as a generic procedure that calls the
|
|
corresponding method on values where @racket[name?] is true. Each method
|
|
must have a required by-position argument that is
|
|
@racket[free-identifier=?] to @racket[name]. This argument is used in
|
|
the generic definition to locate the specialization.
|
|
|
|
The optional @racket[defined-table] argument should be an identifier.
|
|
@racket[define-generics] will bind it to a procedure that takes an instance of
|
|
the generics and returns an immutable hash-table that maps symbols
|
|
corresponding to method names to booleans representing whether or not that
|
|
method is implemented by that instance. The intended use case for this table is
|
|
to allow higher-level APIs to adapt their behavior depending on method
|
|
availability.
|
|
|
|
}
|
|
|
|
@defform[(define/generic local-name method-name)
|
|
#:contracts
|
|
([local-name identifier?]
|
|
[method-name identifier?])]{
|
|
|
|
When used inside the method definitions associated with the
|
|
@racket[#:methods] keyword, binds @racket[local-name] to
|
|
the generic for @racket[method-name]. This is useful for method
|
|
specializations to use the generic methods on other values.
|
|
|
|
Syntactically an error when used outside the definitions associated
|
|
with @racket[#:methods].
|
|
|
|
}
|
|
|
|
@; Examples
|
|
@(require scribble/eval)
|
|
@(define (new-evaluator)
|
|
(let* ([e (make-base-eval)])
|
|
(e '(require (for-syntax racket/base)
|
|
racket/generic))
|
|
e))
|
|
|
|
@(define evaluator (new-evaluator))
|
|
|
|
@examples[#:eval evaluator
|
|
(define-generics printable
|
|
(gen-print printable [port])
|
|
(gen-port-print port printable)
|
|
(gen-print* printable [port] #:width width #:height [height]))
|
|
|
|
(define-struct num (v)
|
|
#:methods gen:printable
|
|
[(define/generic super-print gen-print)
|
|
(define (gen-print n [port (current-output-port)])
|
|
(fprintf port "Num: ~a" (num-v n)))
|
|
(define (gen-port-print port n)
|
|
(super-print n port))
|
|
(define (gen-print* n [port (current-output-port)]
|
|
#:width w #:height [h 0])
|
|
(fprintf port "Num (~ax~a): ~a" w h (num-v n)))])
|
|
|
|
(define-struct bool (v)
|
|
#:methods gen:printable
|
|
[(define/generic super-print gen-print)
|
|
(define (gen-print b [port (current-output-port)])
|
|
(fprintf port "Bool: ~a"
|
|
(if (bool-v b) "Yes" "No")))
|
|
(define (gen-port-print port b)
|
|
(super-print b port))
|
|
(define (gen-print* b [port (current-output-port)]
|
|
#:width w #:height [h 0])
|
|
(fprintf port "Bool (~ax~a): ~a" w h
|
|
(if (bool-v b) "Yes" "No")))])
|
|
|
|
(define x (make-num 10))
|
|
(gen-print x)
|
|
(gen-port-print (current-output-port) x)
|
|
(gen-print* x #:width 100 #:height 90)
|
|
|
|
(define y (make-bool #t))
|
|
(gen-print y)
|
|
(gen-port-print (current-output-port) y)
|
|
(gen-print* y #:width 100 #:height 90)
|
|
]
|
|
|
|
@close-eval[evaluator]
|