#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]