The interface of a unit is described in terms of signatures. Each signature is defined (normally within a module) using define-signature. For example, the following signature, placed in a "toy-factory-sig.rkt" file, describes the exports of a component that implements a toy factory:
By convention, signature names with ^.
#lang racket (define-signature toy-factory^ (build-toys ; (integer? -> (listof toy?)) repaint ; (toy? symbol? -> toy?) toy? ; (any/c -> boolean?) toy-color)) ; (toy? -> symbol?) (provide toy-factory^)
By convention, unit names with @.
#lang racket (require "toy-factory-sig.rkt") (define-unit simple-factory@ (import) (export toy-factory^) (printf "Factory started.\n") (define-struct toy (color) #:transparent) (define (build-toys n) (for/list ([i (in-range n)]) (make-toy 'blue))) (define (repaint t col) (make-toy col))) (provide simple-factory@)
The toy-factory^ signature also could be referenced by a unit that needs a toy factory to implement something else. In that case, toy-factory^ would be named in an import clause. For example, a toy store would get toys from a toy factory. (Suppose, for the sake of an example with interesting features, that the store is willing to sell only toys in a particular color.)
#lang racket (define-signature toy-store^ (store-color ; (-> symbol?) stock! ; (integer? -> void?) get-inventory)) ; (-> (listof toy?)) (provide toy-store^)
#lang racket (require "toy-store-sig.rkt" "toy-factory-sig.rkt") (define-unit toy-store@ (import toy-factory^) (export toy-store^) (define inventory null) (define (store-color) 'green) (define (maybe-repaint t) (if (eq? (toy-color t) (store-color)) t (repaint t (store-color)))) (define (stock! n) (set! inventory (append inventory (map maybe-repaint (build-toys n))))) (define (get-inventory) inventory)) (provide toy-store@)
Note that "toy-store-unit.rkt" imports "toy-factory-sig.rkt", but not "simple-factory-unit.rkt". Consequently, the toy-store@ unit relies only on the specification of a toy factory, not on a specific implementation.