So far in the course, we have been writing small programs that are an amalgamation of mostly unrelated functions, all placed in a single file. For larger programs, this approach does not scale up particularly well. We have placed different parts of a complex computation into different functions to delineate multiple steps in the overall calculation. What is the more coarse grained way to divide up different parts of our code (combinations of functions) that play different roles in our overall program? They should be placed in different modules of code, and stored in different files.

There are various benefits to modularity. Grouping related code into dedicated files makes code browsing easier and is a stylistic benefit. The other main benefit, and arguably the primary one, is that the same module can be used with different programs. Without modules, a set of desired functions must be carefully excised from a larger file each time it is to be integrated into a new application; with modules, it simply remains in the file where it has been all along and need only be, in a sense, plugged into its new program. A third benefit of this approach is that only a single copy of the shared code is maintained, rather than several copied-and-pasted versions which may begin to diverge, and require separate maintenance.

You have already interacted with the module system. The racket-tests module provides features like check-expect; the cs151-core and related image and universe modules smooth sharp corners off of certain language features. But in this section, we describe how to define your own modules to improve the organization of larger programs.

Fortunately, it is not particularly difficult to interact with Racket’s module system. To create a module, you place related type definitions (define-type and define-struct forms) and functions into one .rkt file. You then determine which parts of the code need to be accessible outside of that file. For instance, you might make your helper functions private — internal to your module — while making the rest of your code externally accessible.

Defining modules

To make a define-type definition externally visible, add a provide directive below it, as in the following example:

(define-type Suit (U 'Spades 'Hearts 'Clubs 'Diamonds))
(provide Suit)

Making a function accessible to code outside the module is similar.

(: red? : Suit -> Boolean)
(define (red? s)
  (or (symbol=? s 'Hearts) (symbol=? s 'Diamonds)))
(provide red?)

To make a struct externally available requires some additional syntax.

(define-struct Point2D
  ([x : Real]
   [y : Real]))
(provide (struct-out Point2D))

Using the struct-out modifier causes all of the synthesized operations to also be exported from the module (e.g., Point2D-x and Point2D-y). If we just wrote

(provide (struct-out Point2D))

then the Point2D type would be available, but it would be abstract.

It is also possible to rename types and operations that are provided in a module. For example, the following code provides the function red-card? to clients of the module, where the implementation of the function is named red? inside the module.

(provide (rename-out [red? red-card?]))

At the top of each module file, indicate the language (#lang typed/racket) and require any other modules (such as cs151-core) that the code in the file needs.

While we have put the provide directives directly following the definitions in these examples, in practice you may want to collect all of the directives together at the bottom of the file so that it is easy to see what definitions are being exported.

Using modules

In any other file that needs to have access to the types, structs, and/or functions declared in a module, require the module. For instance, if the file point.rkt contains the Point2D type and routines for manipulating two-dimensional points, then a file that works with shapes might have:

(require "point.rkt")

alongside its other require directives. It can then access the types and functions defined in the other file as if they were part of the same file (as long as they were made externally visible using provide).

Last updated 2019-12-29 10:59:14 -0600
Table of Contents | Back to CS151 Home