1.13 Programs and Modules🔗ℹ

When you write a program using #lang shplait, you are technically defining a module. A Shplait module contains a mixture of expressions and definitions. The expressions are evaluated in order, and the value of each expression is printed after the expression is evaluated (unless the result value has type Void). The order of function definitions doesn’t matter, as long as a function definition appears before any expression that eventually calls the function.

#lang shplait

 

fun num_is_odd(x):

  if x == 0

  | #false

  | num_is_even(x-1)

 

num_is_odd(0) // ok

#//

num_is_odd(1) // won't work, because it needs num_is_even

 

fun num_is_even(x):

  if x == 0

  | #true

  | num_is_odd(x-1)

 

num_is_even(1) // ok

num_is_odd(1) // ok

Note the use of #// in the example above. A #// comments out the entire form that follows it, which is handy for commenting out a definition or expression, even when the definition or expression spans multiple lines.

Modules written with the module form can be nested in other modules. A nested module is called a submodule. More precisely, the module form creates a submodule by merging all modules that use the same name. A typical use of module is to move all of a program’s tests into a test submodule.

#lang shplait

 

fun num_is_odd(x):

  if x == 0

  | #false

  | num_is_even(x-1)

 

module test:

  num_is_odd(0) // ok

  num_is_odd(1) // ok

 

fun num_is_even(x):

  if x == 0

  | #true

  | num_is_odd(x-1)

 

module test:

  num_is_even(1) // ok

  num_is_odd(1) // ok

The submodule name test is special, because DrRacket automatically runs a test submodule (if one is present) after running the enclosing module. In the above example, since the test submodule is run after the encloding module that defines num_is_odd and num_is_even, the tests can use all of the functions. Another advantage of putting tests in a test submodule is that you can turn off the tests. In DrRacket’s Language menu, select Choose Language, click Show Details, click Submodules to run, and then uncheck the test item.

A Shplait module’s definitions are automatically exported from the module. You can import the definitions of another module by using the import form, typically with a string that is a relative path to the module to import. To access the imported names, use the file name plus . as a prefix, or add open before the file name to make imported names visible without a prefix.

// math.rhm

#lang shplait

 

def pi = 3 // we still only have integers

def tau = pi + pi

// circle.rhm

#lang shplait

import:

  "math.rhm"

 

fun circle_area(r):

  math.pi * r * r

fun circumference(r):

  r * math.tau

// sphere.rhm

#lang shplait

import:

  open: "math.rhm"

 

fun sphere_volume(r):

  (4/3) * pi * r * r * r

A submodule created by module automatically imports the bindings of the enclosing module, which is why module test submodules can automatically access definitions for testing. In contrast, if you write definitions inside module test, then the definitions can be used for tests in any module test, but the enclosing module will not see the definitions.