A function is a set of expressions that’s only evaluated when explicitly invoked at . Optionally, a function can take arguments as input, and return values as output. Within a program, functions are used for three overlapping purposes:
To delay the evaluation of a set of expressions.
To allow repeated evaluation of a set of expressions.
To generalize a set of expressions by using input arguments.
Functions can be passed around Racket programs like any other value:
If a function is created with define, it uses an for its name. A function can also be created without a name (that is, as an ) using lambda, which can be spelled λ. A lambda expression can appear anywhere that a function name is allowed:
A lambda expression can also be bound (assigned) to an identifier. The two definitions below are equivalent (in fact, under the hood, Racket rewrites the first style of define in the lambda style):
In the Racket documentation, functions are also called . A function that takes one argument and returns a Boolean (like even? or empty?) is often called a . You can test whether a value is a function with the procedure? predicate.
Lambda is sometimes used generically as a synonym for function, due to Racket’s association with the . A lambda without arguments that wraps an expression so it can be evaluated later is sometimes called a thunk.
A function can be invoked in two ways:
Directly in an expression. Any code written as a parenthesized expression like (func arg1 arg2 ...) will be treated as a function call, where func is the function name and arg1 arg2 ... are passed as its arguments.
Though prefix notation is most common, Racket supports a limited form of in function expressions. You can put the function name between dots, and regardless of where it appears, it will be treated as the first element of the expression:
Indirectly, by passing it as an argument to another function. For example, apply takes a function and a list of values like (list arg1 arg2 ...) and passes those values as arguments to the function:
For another example, filter takes two arguments—a function and a list of values—and applies the function to each value, generating a new list without the values that return #f:
As mentioned above, you can always use a lambda expression in place of a function name:
By default, all function arguments are , meaning they’re bound to identifiers within the function according to where they appear in the argument list. Racket also supports named that can appear anywhere in the argument list:
Any positional or keyword argument can have a default value by specifying that value in square brackets (as usual, arguments without default values must come first):
Functions can also be defined to take any number of arguments with a , which means “put the rest of the arguments here.” A rest argument always ends up holding a list. A rest argument can be combined with other argument types, but must appear last, and cannot have a default value:
In Racket, it’s common to use rest arguments when a function can be logically extended to take any number of arguments—even for operations that in other languages take only two arguments:
If a declared rest argument is unneeded, it becomes the empty list:
Every function has a return value. The value returned is the last value that appears in the function. There is no return statement.
If the function doesn’t have an explicit last value, then the constant #<void> is returned, which can be tested with void?. So #<void> is both a nothing and a something:
It’s rarely necessary, but a function can return multiple values, using values. These functions must be called from program positions where multiple values are accepted (for instance, with define-values rather than define):
1 2 3 4 5 6 7
Within the Racket library, certain sets of functions have special naming conventions. Adopting these conventions for your own code isn’t mandatory, but knowing them will help you understand existing Racket code, and using them will help others understand yours:
If a function handles one input argument or returns one value, a variant function that handles multiple input arguments or multiple return values uses the same name, but suffixed with *. For instance: regexp-match vs. regexp-match*, string-append vs. string-append*, list vs. list*. The * suffix can also denote a variant that treats multiple arguments as nested rather than parallel: for vs. for*, let vs. let*.
A parameter is a special kind of function that approximates the behavior of a global variable. An ordinary Racket value can be mutated with set!. But there are certain restrictions on when variables can be mutated (say, across module boundaries). Whereas parameters have no such restrictions.
A parameter is created with make-parameter and an initial value. After that, calling the parameter without an argument retrieves its current value. Passing the parameter an argument changes its value:
One advantage of parameters over global variables is that they can be changed within the boundaries of a given expression by wrapping that expression with parameterize:
1 2 3 4 5
Here, parameterize changes the value of current-list only for the nested subexpressions. After the parameterize exits, current-list automatically reverts to its previous value.
Racket belongs to a category of languages—also including Scheme, Common Lisp, and Haskell—that are associated with . Functional programming is a style of programming where functions receive certain data as input, process only that data, and return a result. (It does not mean “programming with functions”—everyone does that.)
In functional programming, programmers avoid two habits common in other languages: mutation (= changing data in-place rather than returning a value) and relying on state (= extra context that’s not provided as input, for instance global variables).
Without state and mutation, the behavior of each function is contained. Thus, it’s easier to prove things about that behavior—in the research sense (if you’re a computer scientist) or in the testing sense (if you’re a programmer).
That said, there are times where the functional-programming approach doesn’t fit, especially with functions that are used for their —for instance, println. So the rule of thumb in Racket is to use functional programming when you can, and depart from it when you must.