Helpful Tidbits
1 Functions as Arguments
2 Signalling Errors
3 Cannot reference an identifier before its definition
7.4.0.13

Helpful Tidbits

John Clements

1 Functions as Arguments

Functions are values just like numbers or strings. Here’s an example that passes a predicate to a function, which then calls it on the string "apple".

#lang typed/racket
 
(require typed/rackunit)
 
;; given a predicate, return 6 if the predicate holds for the string
;; "apple", return 2 otherwise
(define (6-if-true-for-apple [pred : (String -> Boolean)]) : Natural
  (cond [(pred "apple") 6]
        [else 2]))
 
;; a predicate that returns true for strings longer than 1 char whose
;; second character is "p"
(define (my-pred [s : String]) : Boolean
  (and (< 1 (string-length s)) (equal? (substring s 1 2) "p")))
 
(check-equal? (6-if-true-for-apple my-pred) 6)
 

2 Signalling Errors

It will save you lots of time in the long run to signal helpful errors in your interpreters, especially since you’re the one who will be staring at them, trying to figure out the problem.

The best function to call is error, but be sure to call it with a symbol as the first argument. When the first argument is a symbol (typically the name of the function that went wrong), the second argument is a format string, much like the ones you’d find in C, except that you don’t need to specify the type of the argument; I suggest using the ~e specifier to get good formatting with a limit on the length.

Here’s an example:

;; only call this function with 16
(define (only-16 [x : Number]) : Number
  (cond [(equal? x 16) 128]
        [else (error 'only-16
                     "expected 16, got: ~e"
                     x)]))
 
(only-16 12)

3 Cannot reference an identifier before its definition

The TL;DR is this:

Move your test cases downward.

Here’s a sample program:

#lang racket
 
(require rackunit)
 
(define (add4 x) (+ 2 (add2 x)))
 
(check-equal? (add4 13) 17)
 
(define (add2 x) (+ 2 x))

(It’s in Racket, just to simplify things.)

Running this program produces this error:

add2: undefined;

 cannot reference an identifier before its definition

So... is it not legal for a function such as add4 to call a later function such as add2? No, that’s fine.

The problem here is that the *test case* calls add2 before add2 has been defined. The solution? Just move the test case downward, so that it is below the function that it calls, and all of the functions that those functions call.

The simple solution here is to move the test cases to the bottom, but it’s often nice during development to put the test cases close to the definition, and move them down later. When you see this error, though, you’ll know what to do.