Seth Barrett

Daily Blog Post: March 6th, 2023

racket3

Mar 6th, 2023

Exploring Racket's Functional Programming Capabilities: Higher-Order Functions and Closures

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.