5.4 Generic Interfaces

► |  | The Racket Reference

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

▼ | 5 | Structures

5.1 | Defining Structure Types: struct
5.2 | Creating Structure Types
5.3 | Structure Type Properties
5.4 | Generic Interfaces
5.5 | Copying and Updating Structures
5.6 | Structure Utilities
5.7 | Structure Type Transformer Binding

On this page:

define-generics
raise-support-error
exn: fail: support
define/ generic
generic-instance/ c
impersonate-generics
chaperone-generics
redirect-generics
make-struct-type-property/ generic
make-generic-struct-type-property

8.16
top contents ← prev up next →

#### 5.4`` Generic Interfaces🔗ℹ

(require racket/generic) | package: base

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
> > generics-opt ...
> > [method-id . kw-formals*] ...
> > generics-opt ...)
> generics-opt |  | = |  | #:defaults ([default-pred? default-impl ...] ...)
> |  | | |  | #:fast-defaults ([fast-pred? fast-impl ...] ...)
> |  | | |  | #:fallbacks [fallback-impl ...]
> |  | | |  | #:defined-predicate defined-pred-id
> |  | | |  | #:defined-table defined-table-id
> |  | | |  | #:derive-property prop-expr prop-value-expr
> |  | | |  | #:requires [required-method-id ...]
> kw-formals* |  | = |  | (arg* ...)
> |  | | |  | (arg* ...+ . rest-id)
> |  | | |  | rest-id
> arg* |  | = |  | arg-id
> |  | | |  | [arg-id]
> |  | | |  | keyword arg-id
> |  | | |  | keyword [arg-id]

Defines the following names, plus any specified by keyword options.

- 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 method that calls the corresponding method on values where id? is true. 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.

- 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. 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.

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

The #:fast-defaults option may be provided at most once. It works the same as #:defaults, except the fast-pred?s are checked before dispatching to the generic method table. This option is intended to provide a fast path for dispatching to built-in datatypes, such as lists and vectors, that do not overlap with structures implementing gen: id.

The #:fallbacks option may be provided at most once. When it is provided, the fallback-impls define fallback method implementations that are used for any instance of the generic interface that does not supply a specific implementation. The syntax of the fallback-impls is the same as the methods provided for the #:methods keyword for struct.

The #:defined-predicate option may be provided at most once. When it is provided, defined-pred-id is defined as a procedure that reports whether a specific instance of the generic interface implements a given set of methods. Specifically, (defined-pred-id v 'name ...) produces #t if v has implementations for each method name, not counting #:fallbacks implementations, and produces #f otherwise. This procedure is intended for use by higher-level APIs to adapt their behavior depending on method availability.

The #:defined-table option may be provided at most once. When it is provided, defined-table-id is defined as a procedure that takes an instance of the generic interface 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 option is deprecated; use #:defined-predicate instead.

The #:derive-property option may be provided any number of times. Each time it is provided, it specifies a structure type property via prop-expr and a value for the property via prop-value-expr. All structures implementing the generic interface via #:methods automatically implement this structure type property using the provided values. When prop-value-expr is executed, each method-id is bound to its specific implementation for the structure type.

The #:requires option may be provided at most once. When it is provided, any instance of the generic interface must supply an implementation of the specified required-method-ids. Otherwise, a compile-time error is raised.

If a value v satisfies id?, then v is a generic instance of gen: id.

If a generic instance v has a corresponding implementation for some method-id provided via #:methods in struct or via #:defaults or #:fast-defaults in define-generics, then method-id is an implemented generic method of v.

If method-id is not an implemented generic method of a generic instance v, and method-id has a fallback implementation that does not raise an exn:fail:support exception when given v, then method-id is a supported generic method of v.

Changed in version 8.7.0.5 of package base: Added the #:requires option.

> procedure (raise-support-error name v) → none/c
> name : symbol?
> v : any/c

Raises an exn:fail:support exception for a generic method called name that does not support the generic instance v.

Example:

> > (raise-support-error 'some-method-name '("arbitrary" "instance" "value"))
> some-method-name: not implemented for '("arbitrary"
> "instance" "value")

> > struct
> > (struct |  | exn:fail:support exn:fail ()
> #:transparent)

Raised for generic methods that do not support the given generic instance.

> syntax (define/generic local-id method-id)

When used inside the method definitions associated with the #:methods, #:fallbacks, #:defaults or #:fast-defaults keywords, 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.

The define/generic form is only allowed inside:

- a #:methods specification in struct (or define-struct)

- the specification of #:fallbacks, #:defaults or #:fast-defaults in define-generics

Using define/generic elsewhere is a syntax error.

Examples:

> > (define-generics printable
> (gen-print printable [port])
> (gen-port-print port printable)
> (gen-print* printable [port] #:width width #:height [height])
> #:defaults ([string?
> (define/generic super-print gen-print)
> (define (gen-print s [port (current-output-port)])
> (fprintf port "String: ~a" s))
> (define (gen-port-print port s)
> ; we can call gen-print alternatively
> (super-print s port))
> (define (gen-print* s [port (current-output-port)]
> #:width w #:height [h 0])
> (fprintf port "String (~ax~a): ~a" w h s))]))
> > (struct num (v)
> #:methods gen:printable
> [(define (gen-print n [port (current-output-port)])
> (fprintf port "Num: ~a" (num-v n)))
> (define (gen-port-print port n)
> (gen-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)))])
> > (struct string+num (v n)
> #:methods gen:printable
> [(define/generic super-print gen-print)
> (define/generic super-print* gen-print*)
> (define (gen-print b [port (current-output-port)])
> (super-print (string+num-v b) port)
> (fprintf port " ")
> (super-print (string+num-n b) port))
> (define (gen-port-print port b)
> (gen-print b port))
> (define (gen-print* b [port (current-output-port)]
> #:width w #:height [h 0])
> (super-print* (string+num-v b) #:width w #:height h)
> (fprintf port " ")
> (super-print* (string+num-n b) #:width w #:height h))])
> > (define x (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 str "Strings are printable too!")
> > (gen-print str)
> String: Strings are printable too!
> > (define y (string+num str x))
> > (gen-print y)
> String: Strings are printable too! Num: 10
> > (gen-port-print (current-output-port) y)
> String: Strings are printable too! Num: 10
> > (gen-print* y #:width 100 #:height 90)
> String (100x90): Strings are printable too! Num (100x90): 10
> > (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?)]))
> num)
> > (define z (make-num-contracted 10))
> > (gen-print* z #:width "not a number" #:height 5)
> make-num-contracted: contract violation
> expected: natural?
> given: "not a number"
> in: the #:width argument of
> method gen-print*
> the range of
> (->
> number?
> (printable/c
> (gen-print
> (->* (printable?) (output-port?) void?))
> (gen-port-print
> (-> output-port? printable? void?))
> (gen-print*
> (->*
> (printable? #:width natural?)
> (output-port? #:height natural?)
> void?))))
> contract from:
> (definition make-num-contracted)
> blaming: top-level
> (assuming the contract is correct)
> at: eval:16:0

> syntax (generic-instance/c gen-id [method-id method-ctc] ...)
> | method-ctc |  | : |  | contract?

Creates a contract that recognizes structures that implement the generic interface gen-id, and constrains their implementations of the specified method-ids with the corresponding method-ctcs.

> > syntax
> > (impersonate-generics gen-id val-expr
> > [method-id method-proc-expr] ...
> > maybe-properties)
> maybe-properties |  | = |  |
> |  | | |  | #:properties props-expr
> > | method-proc-expr |  | : |  | (any/c . -> . any/c)
> > | props-expr |  | : |  | (list/c impersonator-property? any/c ... ...)

Creates an impersonator of val-expr, which must be a structure that implements the generic interface gen-id. The impersonator applies the results of the method-proc-exprs to the structure’s implementation of the corresponding method-ids, and replaces the method implementation with the result.

A props-expr can provide properties to attach to the impersonator. The result of props-expr must be a list with an even number of elements, where the first element of the list is an impersonator property, the second element is its value, and so on.

Changed in version 6.1.1.8 of package base: Added #:properties.

> > syntax
> > (chaperone-generics gen-id val-expr
> > [method-id method-proc-expr] ...
> > maybe-properties)

Like impersonate-generics, but creates a chaperone of val-expr, which must be a structure that implements the generic interface gen-id. The chaperone applies the specified method-procs to the structure’s implementation of the corresponding method-ids, and replaces the method implementation with the result, which must be a chaperone of the original.

> > syntax
> > (redirect-generics mode gen-id val-expr
> > [method-id method-proc-expr] ...
> > maybe-properties)

Like impersonate-generics, but creates an impersonator of val-expr if mode evaluates to #f, or creates a chaperone of val-expr otherwise.

> > syntax
> > (make-struct-type-property/generic
> > name-expr
> > maybe-guard-expr
> > maybe-supers-expr
> > maybe-can-impersonate?-expr
> > property-option
> > ...)
> maybe-guard-expr |  | = |  |
> |  | | |  | guard-expr
> maybe-supers-expr |  | = |  |
> |  | | |  | supers-expr
> maybe-can-impersonate?-expr |  | = |  |
> |  | | |  | can-impersonate?-expr
> property-option |  | = |  | #:property prop-expr val-expr
> |  | | |  | #:methods gen:name-id method-defs
> method-defs |  | = |  | (definition ...)
> > | name-expr |  | : |  | symbol?
> > | guard-expr |  | : |  | (or/c procedure? #f 'can-impersonate)
> > | supers-expr |  | : |  | (listof (cons/c struct-type-property? (-> any/c any/c)))
> > | can-impersonate?-expr |  | : |  | any/c
> > | prop-expr |  | : |  | struct-type-property?
> > | val-expr |  | : |  | any/c

Creates a new structure type property and returns three values, just like make-struct-type-property would:

- a structure type property descriptor

- a property predicate procedure

- a property accessor procedure

Any struct that implements this property will also implement the properties and generic interfaces given in the #:property and #:methods declarations. The property val-exprs and method-defs are evaluated eagerly when the property is created, not when it is attached to a structure type.

> > syntax
> > (make-generic-struct-type-property
> > gen:name-id
> > method-def
> > ...)

Creates a new structure type property and returns the structure type property descriptor.

Any struct that implements this property will also implement the generic interface given by gen:name-id with the given method-defs. The method-defs are evaluated eagerly when the property is created, not when it is attached to a structure type.

top contents ← prev up next →