Mar 6th, 2023
Welcome back to our Racket series. In the previous post, we covered some of the basics of Racket, including data structures, functions, and control structures. In this post, we will dive deeper into Racket's functional programming capabilities, including higher-order functions and closures.
Higher-Order Functions
In Racket, functions are first-class citizens, which means they can be passed as arguments to other functions and returned as values from functions. Functions that take other functions as arguments or return functions as values are called higher-order functions.
Here's an example of a higher-order function that takes a function f
and an integer n
as arguments and applies f
to n
n
times:
(define (repeat f n) (if (= n 0) (lambda (x) x) (compose f (repeat f (- n 1))))) (define (square x) (* x x)) ((repeat square 3) 2) ; returns 256
In this example, we define a function repeat
that takes a function f
and an integer n
.
If n
is 0, repeat
returns the identity function (i.e., a function that returns its argument unchanged).
Otherwise, repeat
composes f
with a new function that is the result of calling repeat
with f
and n-1
.
We then define a function square
that takes a number x
and returns its square.
Finally, we call repeat
with square
and 3
as arguments, and we pass 2
to the resulting function to get the answer 256
.
Closures
A closure is a function that has access to variables in its lexical scope, even after the enclosing function has returned. Closures are useful for creating functions with persistent state.
Here's an example of a closure that takes an initial value x
and returns a function that adds x
to its argument:
(define (make-adder x) (lambda (y) (+ x y))) (define add-5 (make-adder 5)) (add-5 10) ; returns 15
In this example, we define a function make-adder
that takes an initial value x
.
make-adder
returns a lambda function that takes an argument y
and returns the sum of x
and y
.
We then define a new function add-5
that is the result of calling make-adder
with 5
.
Finally, we call add-5
with 10
as an argument, and we get the answer 15
.
Conclusion
In this post, we've explored Racket's functional programming capabilities, including higher-order functions and closures. We've also provided some code examples to illustrate these concepts. In the next post, we will discuss Racket's macro system and its power to extend the language.