Racket Basics

Racket code looks different from many of the languages you are used to, but the semantics are not much more complicated. In Racket, most expressions are wrapped in parenthesis (), which is a more uniform representation of the tree structure of programs. For HW3, you will only need a fraction of the syntax and primitives of Racket. One of the biggest differences from Python is that every expression returns a value (like the lambda calculus), but there are ways to execute sequenctial blocks. We are providing a list of the most useful Racket constructs, and their Python equivalents. The format is:

(racket code)
python equivalent

Calling functions

(f x y)
f(x,y)
(g)
g()
((g))
(g())()

Note that the number of parenthesis is very important. g just returns the function g (as in the lambda calculus), (g) calls the function g with no arguments, and ((g)) calls g, and then calls the function g returns.

Also, Racket is more permissive with characters allowed in identifiers. In Racket, number? is one atomic name, the ? carries no syntactic meaning.

Local variables

Racket has an extend form of let ... in ... as discussed in lecture:

(let* ([x e1]
       [y e2]
       [z e3])
       (f x)
       (g y)
       z
)
x = e1
y = e2
z = e3
f(x)
g(y)
return z

We use return here in Python to specify what the value of the overall (let* ...) is in Racket. There is no direct return in Racket, only the result of evaluating an expression. Note that the parenthesis and brackets have to appear exactly as above. The value of x is available in e2 and later, an similarly for other variables. A single let* allows some number of variables, followed by some number of expressions (including one), the last of which is the result of the let*. If you want to call a function in the middle of defining variables, you can assign it to a dummy variable ([_ (f x)]). The let* construct is also useful for sequencing several function calls without defining variables (as in (let* () (f x) (g y))).

Defining Functions

(define (f a b) ...)
def f(a, b):
    return ...
(lambda (x) ...)
lambda x: ...
(lambda () ...)
lambda: ...

define allows self-recursive definitions, and lambda works similar to the lambda calculus except you can have any number of arguments (including zero).

Conditionals

(cond [t1 e1] [t2 e2] [else e3])
if t1:
    return e1
elif t2:
    return e2
else:
    return e3

Note we use return in Python to specify what the value of (cond ...) as before.

(if c a b)
a if c else b

To make an expression evaluate to value in a variable, put the value without parenthesis. For example, (if (number? a) a 0) is an expression that returns a if it is a number and zero otherwise. Using (a) instead would attempt to call a as a function, which gives an error if a is a number.

Use #t for True and #f for False.

Lists

(list a b c)
[a, b, c]
(first l)
l[0]
(second l)
l[1]
(third l)
l[2]
(rest l)
l[1:]
(null? l)
len(l) == 0

Numbers and Strings

(number? x)
isinstance(x, int)
(+ a b)
a + b

Also works for -, *, /. Nested expressions like 2 * x + 5 look like (+ (* 2 x) 5).

(equal? x y)
x == y

Also works for comparing strings.

(printf "~a\n" x)
print(x)

Don't forget the \n for printing newlines.

Interpreting Errors

The error messages in Racket can be confusing at first. For example, if you write (if (number? a) (a) ...) when a=1, the error is:

application: not a procedure; expected a procedure that can be applied to arguments
  given: 1
  arguments...: [none]
  ...

Here the given: is telling us that we are trying to call 1, which is the value of a. As described before, we can fix this issue by dropping parenthesis.

Type errors, such as not passing a number to +, look like:

+: contract violation
  expected: number?
  given: ...
  argument position: 2nd
  ...

Calling a function with an incorrect number of arguments looks like:

 the expected number of arguments does not match the given number
  expected: 0
  given: 1
  arguments...:
  ...

Unfortunately, the Racket line numbers given in error do not necessarily indicate the exact line where the error occurred, but almost always indicates the correct function. Be sure to read your code carefully to look for the kind of error on lines other than the ones indicated.