5 DrRacket: Start Me Up

When DrRacket starts up, it configures itself from information it finds in so-called “info” files in the various library collections. If such an info file specifies that the library wishes to plug in a component, DrRacket loads the component. The loading process can register mixins with DrRacket, which are added to the IDE’s top-level frame at the appropriate moment.

"drracket-startup.rkt"

#lang racket
 
(require "drracket-plugin.rkt")
 
; collect all plugins from the info.rkt files in tool directories
(define plugins (list "drracket-info.rkt"))
 
; dynamically load the plugins so that they can register themselves
(for ([plugin (in-list plugins)])
  (dynamic-require plugin #f))
 
(define the-top-level-frame%
  (send mixin-registry get-drr-frame%))
 
(define the-top-level-frame
  (new the-top-level-frame% [label "DrRacket"]))
 
(send the-top-level-frame show #t)

Figure 11: The start-up code for DrRacket

Figure 11 is a sketch of DrRacket’s start-up module. The module first imports functionality for registering the mixins. The second line specifies a list of component files for this sample code; in reality, this step searches directories for info files and retrieves the desired information from those. Once the plug-in information is available, the designated components are dynamically loaded. This dynamic-require step allows components to initialize themselves and to register mixins with DrRacket as needed. Since a dynamically required module shares state with an already loaded module, the registry works in principle in a straightforward manner:
  • a component uses a register method to add information, and

  • DrRacket uses a retrieval method, dubbed get-drr-frame%, to obtain the information.

Figure 12 displays the full definition of the registry class and its single instance.

With first-class classes, a singleton pattern is just a class expression nested in new.

"drracket-plugin.rkt"

#lang racket/gui
 
(provide
 ; object with two public methods:
 ; register and get-drr-frame%
 mixin-registry)
 
(define mixin-registry
  (new
   (class object%
     (super-new)
 
     ; Window -> Window
     (define (mixin x) x)
 
     ; [Maybe Window]
     (define drr-frame% #f)
 
     ; (Window -> Window) -> Void
     ; register another mixin m with drracket
     ; effect: accumulate m with mixin via function composition
     (define/public (register m)
       (cond
         [drr-frame% (error 'register-mixin "too late")]
         [else (set! mixin (compose mixin m))]))
 
     ; -> Window
     ; create the top-level frame from current state of mixin
     ; effect: set drr-frame% to its final state, a top-level frame
     (define/public (get-drr-frame%)
       (unless drr-frame%
         (set! drr-frame% (mixin frame%)))
       drr-frame%))))

Figure 12: DrRacket's plug-in registry

The information that DrRacket actually retrieves is the class from which it then creates its top-level window. See the last few lines of figure 11. In doing so, DrRacket relies on the protocol that mixin-registry in figure 12 implements. As you can see, the register method consumes a mixin function and composes it with the function created so far, stored in the private mixin field. Since the field’s initial value is the identity function, DrRacket uses the plain frame% when it doesn’t find any plug-ins.

When DrRacket uses get-drr-frame%, the method sets a flag. The register method checks this flag before accepting any mixins. By setting the flag, DrRacket disallows the addition of components after the top-level frame is popped up.

"drracket-info.rkt"

#lang racket
 
(require "drracket-plugin.rkt")
 
; Window -> Window
; a mixin that adds a message to the top-level frame
(define (sample-mixin base%)
  (class base%
    (super-new)
    (new message% [parent this] [label "mixin added"])))
 
(send mixin-registry register sample-mixin)

Figure 13: A DrRacket plug-in component

Figure 13 finally displays a simple component file. The module requires the plug-in registry and uses its register method to add the sample-mixin functionality. The latter is a procedure that accepts a top-level window and returns a class that extends it. This derived class simply pops up a message window displaying the string "mixin added".

Of course, realistic plug-ins perform many other computations. For the actual protocol, see the DrRacket Plugins documentation and how it relies on dynamic composition of mixins. Use the complete code for from the above figures to see for yourself how DrRacket’s protocol functions.

Additional Information A publication in the proceedings of the Asian Programming Languages, Applications, and Systems conference has additional information about Racket classes, especially how to mimic existing idioms with them.