1.6 Definitions🔗ℹ

The def form defines an identifier to be a synonym for a value:

> def pi = 3 // close enough, since we only have integers

> pi

- Int

3

> def tau = pi + pi

> tau

- Int

6

You can use either = or : to separate the defined name from its value. Use = when everything is on one line, and use : (with the usual rule for indentation after :) when multiple lines are needed.

The fun form defines a function. The new function’s name is followed by an open parenthesis, a comma-separated sequence of argument names, and a closing parenthesis. A : separates the arguments from body of the function.

> fun circle_area(r):

    pi * r * r

> circle_area(10)

- Int

300

Since Getting Started, we have been evaluating forms only in DrRacket’s bottom area, which is also known as the interactions area. Definitions normally go in the top area—which is known as the definitions area, naturally.

Put these two definitions in the definitions area:

fun num_is_odd(x):

  if x == 0

  | #false

  | num_is_even(x-1)

fun num_is_even(x):

  if x == 0

  | #true

  | num_is_odd(x-1)

Click Run. The functions num_is_odd and num_is_even are now available in the interactions area:

> num_is_odd

- Int -> Boolean

#<function:num_is_odd>

> num_is_odd(12)

- Boolean

#false

In our definitions of pi and tau, Shplait inferred that the newly defined names have type Int. It also inferred that num_is_odd has type Int -> Boolean. Programs are often easier to read and understand if you write the type that would otherwise be inferred. Declaring types can sometimes help improve or localize error messages when Shplait’s attempt to infer a type fails, since inference can otherwise end up depending on the whole program.

Declare a type in def by writing :: and a type after the defined identifier:

def groceries :: Listof(String) = ["milk", "cookies"]

For a function, attach a type to an argument using :: plus the type. Write the function’s result type with :: after the argument parentheses but before the : for the function’s body.

fun starts_milk(items :: Listof(String)) :: Boolean:

  is_cons(items) && first(items) == "milk"

You can declare local functions and constants by using the block form. Use : immediately after block, and then write any number of definitions and expressions, each on its own line. The last form in a block body must be an expression, and that expression supplies the result for the whole block form.

> block:

    def pi_ish = 3

    fun approx_circle_area(r):

      pi_ish * r * r

    approx_circle_area(2)

- Int

12

> pi_ish // not visible outside the block

pi_ish: unbound identifier;

 also, no #%top syntax transformer is bound

> approx_cirle_area // not visible outside the block

approx_cirle_area: unbound identifier;

 also, no #%top syntax transformer is bound

The block could be used inside a function to define a helper function or to avoid a repeated computation involving the function arguments. In cases like the body of fun, however, there is an implicit block, so you can just write local definitions there directly.

fun discard_first_if_fruit(items):

  def a = first(items)

  cond

  | a == "apple": rest(items)

  | a == "banana": rest(items)

  | ~else: items

> discard_first_if_fruit(["apple", "potato"])

- Listof(String)

["potato"]

> discard_first_if_fruit(["banana", "potato"])

- Listof(String)

["potato"]

> discard_first_if_fruit(["potato", "apple"])

- Listof(String)

["potato", "apple"]

The let and letrec forms are similar to block with just one definition. The discard_first_if_fruit example above can be equivalently written using let:

fun discard_first_if_fruit(items):

  let a = first(items):

    cond

    | a == "apple": rest(items)

    | a == "banana": rest(items)

    | ~else: items

Although you can write multiple expressions in a block, since those expressions other than the last one do not contribute to the result of the block, those expressions must have type Void. The intent is to allow printing or other side-effecting expressions while disallowing expressions whose result is ignored (perhaps accidentally).

> block:

    println("Adding...")

    1+2

- Int

Adding...

3

> block:

    3+4 // not ok to ignore Int result

    1+2

typecheck failed: Void vs. Int