#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 id [#:defined-table defined-table-id]
                [method-id . kw-formals*]
                ...
                maybe-defaults)
              ([kw-formals* (arg* ...)
                            (arg* ...+ . rest-id)
                            rest-id]
               [arg* arg-id
                     [arg-id]
                     (code:line keyword arg-id)
                     (code:line keyword [arg-id])]
               [maybe-defaults (code:line)
                               (code:line #:defaults ([pred?
                                                       method-impl ...]
                                                      ...))])]{

Defines 

@itemlist[

 @item{@racketidfont{gen:}@racket[id] as a transformer binding for
       the static information about a new generic interface;}

 @item{@racket[id]@racketidfont{?} as a predicate identifying
       instances of structure types that implement this generic group; and}

 @item{each @racket[method-id] as a generic procedure that calls the
       corresponding method on values where
       @racket[id]@racketidfont{?} is true.}

 @item{@racket[id]@racketidfont{/c} as a contract combinator that
       recognizes instances of structure types which implement the
       @racketidfont{gen:}@racket[id] generic interface. The combinator
       takes pairs of @racket[method-id]s and contracts. The contracts
       will be applied to each of the corresponding method implementations.}

]

Each @racket[method-id]'s @racket[kw-formals*] must include a required
by-position argument that is @racket[free-identifier=?] to
@racket[id]. That argument is used in the generic definition to
locate the specialization.

When @racket[defined-table-id] is provided, it is defined as a
procedure that takes an instance of the generics and returns an
immutable @tech{hash table} that maps symbols corresponding to method
names to booleans representing whether or not that method is
implemented by the instance. This table is intended for use by
higher-level APIs to adapt their behavior depending on method
availability.

When @racket[maybe-defaults] is provided, each generic function
uses @racket[pred?]s to dispatch to the given default implementations,
@racket[method-impl]s, if dispatching to the generic method table fails.
The syntax of the @racket[method-impl]s is the same as the methods
provided for the @racket[#:methods] keyword for @racket[struct].}

The @racket[id]@racketidfont{/c} combinator is intended to be used to
contract the range of a constructor procedure for a struct type that
implements the generic interface.

@defform[(define/generic local-id method-id)
         #:contracts
         ([local-id identifier?]
          [method-id identifier?])]{

When used inside the method definitions associated with the
@racket[#:methods] keyword, binds @racket[local-id] to the generic for
@racket[method-id]. This form is useful for method specializations to
use generic methods (as opposed to the local specialization) on other
values.

Using the @racket[define/generic] form outside a @racket[#:methods]
specification in @racket[struct] (or @racket[define-struct]) is an
syntax error.}


@; Examples
@(require scribble/eval)
@(define (new-evaluator)
   (let* ([e (make-base-eval)])
     (e '(require (for-syntax racket/base)
                  racket/contract
                  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)

(define/contract make-num-contracted
  (-> number?
      (printable/c
        [gen-print (->* (printable?) (output-port?) void?)]
        [gen-port-print (-> output-port? printable? void?)]
        [gen-print* (->* (printable? #:width exact-nonnegative-integer?)
                         (output-port? #:height exact-nonnegative-integer?)
                         void?)]))
   make-num)

(define z (make-num-contracted 10))
(gen-print* z #:width "not a number" #:height 5)
]

@close-eval[evaluator]
