4.4 Generic Interfaces

► |  | The Racket Reference

1 | Language Model
2 | Syntactic Forms
3 | Datatypes
4 | Structures
5 | Classes and Objects
6 | Units
7 | Contracts
8 | Pattern Matching
9 | Control Flow
10 | Concurrency and Parallelism
11 | Macros
12 | Input and Output
13 | Reflection and Security
14 | Operating System
15 | Memory Management
16 | Unsafe Operations
17 | Running Racket
| Bibliography
| Index

▼ | 4 | Structures

4.1 | Defining Structure Types: struct
4.2 | Creating Structure Types
4.3 | Structure Type Properties
4.4 | Generic Interfaces
4.5 | Copying and Updating Structures
4.6 | Structure Utilities
4.7 | Structure Type Transformer Binding

On this page:

define-generics
define/ generic

5.3.6
top ← prev up next →

#### 4.4`` Generic Interfaces

(require racket/generic)

A generic interface allows per-type methods to be associated with generic functions. Generic functions are defined using a define-generics form. Method implementations for a structure type are defined using the #:methods keyword (see Defining Structure Types: struct).

> > syntax
> > (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]
> |  | | |  | keyword arg-id
> |  | | |  | keyword [arg-id]
> maybe-defaults |  | = |  |
> #:defaults ([pred?
> method-impl ...]
> ...)

Defines

- gen: id as a transformer binding for the static information about a new generic interface;

- id? as a predicate identifying instances of structure types that implement this generic group; and

- each method-id as a generic procedure that calls the corresponding method on values where id? is true.

- id/c as a contract combinator that recognizes instances of structure types which implement the gen: id generic interface. The combinator takes pairs of method-ids and contracts. The contracts will be applied to each of the corresponding method implementations.

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

When defined-table-id is provided, it is defined as 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 the instance. This table is intended for use by higher-level APIs to adapt their behavior depending on method availability.

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

The id/c combinator is intended to be used to contract the range of a constructor procedure for a struct type that implements the generic interface.

> syntax (define/generic local-id method-id)
> > | local-id |  | : |  | identifier?
> > | method-id |  | : |  | identifier?

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

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

Examples:
> > (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)
> Num: 10
> > (gen-port-print (current-output-port) x)
> Num: 10
> > (gen-print* x #:width 100 #:height 90)
> Num (100x90): 10
> > (define y (make-bool #t))
> > (gen-print y)
> Bool: Yes
> > (gen-port-print (current-output-port) y)
> Bool: Yes
> > (gen-print* y #:width 100 #:height 90)
> Bool (100x90): Yes
> > (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)
> make-num-contracted: contract violation
> expected: exact-nonnegative-integer?
> given: "not a number"
> in: the #:width argument of
> the gen-print* method of
> the range of
> (->
> 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?))))
> contract from:
> (definition make-num-contracted)
> blaming: top-level
> at: eval:13.0

top ← prev up next →