The main command here is define, the two main usages are to bind a value to the result of an expression, and to bind an id to a procedure.

To bind an id to the value of an expression do

(define x 3)

To bind an id to a procedure you can do

(define (my-func arg1 arg2) (+ arg1 arg2))

Note that this is equivalent to:

(define my-func (λ (arg1 arg2) (+ arg1 arg2))

Local Name Bindings

The Let expression allows us to make local name bindings, but the evaluation order of the bindings are done in some undefined order, meaning that they cannot depend on eachother.

Let in Racket

What is the result of the following code:

(define a 100)
(define b 100)
(define c 100)
(let ([a 5] [b (+ a a)] [c (+ a b)]) (list a b c) )

Since the bindings of a let expression cannot depend on eachother, then previously computed values are not used until we are inside the body, thus we have the following

Suppose that c gets bound to (+ a b) first, thus c' becomes 200, suppose a is bound next thus a' becomes 5, then suppose b is bound to (+ a a), since we don't use previously computed values, we still use a = 100, to get that b' is 200. Now we evaluate the body with these ticked variables to obtain '(5 200 200).

Note that no matter what order they are evaluated you always will get the same answer.

Let Error

Why does the following code work?

(define sq-cube
(lambda (x)
(let ([sqr (* x x)] [cube (* x sqr)]) (list sqr cube))
)
)

It won't work because there is no guarentee in which order the identifiers are bound, and thus if the second is bound first, sqr would be undefined.

We can fix the above problem by nesting let statements or by using the let* expression which was defined exactly for having sequential let statements.

Also note that local name bindings do not update values, so if we had the following code

(define a 10)
(let ([a 5]) (+ a 100))

Then the value of a is still 10. In general pure functions will never update values, which implies that every time a given function takes in a specfic set of values it will always return the same result and there are no side effects.

If no values ever change, then how do we actually do anything meaningful? Recursion.

Symbols

Symbols can be thought of like enums.

Macros

(define-syntax list-of ; this is the initial name of macro
(syntax-rules (for in) ; this are reserved keywords, match them literally
(
(list-of result-element-expression for element-name in a-list) ; match this
(map (λ (element-name) result-element-expression) a-list) ; map it to this
)
)
)

Then (list-of e for x in ls) will get replaced with (map (λ (x) e) ls)