Closures

A closure is a local function that “closes” over the a value defined outside of the local itself.

(define (wider-than-only w loi)
	(local [(wider-than? i)
				(> (image-width i) w)]
	(filter wider-than? loi)))

Here, wider-than? is a “closure” because it “closes” over the value of w passed to wider-than-only.

Using locals

Consider a scenario where we have a list of images [I1, I2, I3 … In], and we are trying to write a filtering function that returns only those that meet a certain size specification - wide images, for instance.

;; (listof Image) -> (listof Image)
;; produce list of only those images that have width > height
(check-expect (wide-only (list I1 I2 I3 I4 I5)) (list I2 I4))
 
;; (define (wide-only loi) empty) ;stub
 
(define (wide-only loi)
	(filter ... loi))

In previous examples we made use of a predefied function wide? that was an appropriate plug-in for this function. Because filter takes a function as one of its argument, we are unable to simply write (> (image-width i) (image-height i)) and plug it into the appropriate position.

The solution, then, is to write a function. Because these are relatively simple, we can define the function as a local:

(define (wide-only loi)
	(local [(define (wide? i)
				(> (image-width i)
					(image-height i)))]
	(filter wide? loi)))

Defining a closure

Imagine a slightly different version of the above function, where instead of finding wide images, we simply want to return the images that meet the condition (> (image-width i) w), where w is an arbitrary value of our choice:

(define (wider-than w loi)
	(filter (> (image-width i) w) loi))

When the body of a function we want to pass to an abstract function refers to a parameter of the outer function, as w does in this case, then the function we want to pass must be defined using local. It cannot be defined at top level, unlike the wide? expample above:

(define (wider-than w loi)
	(local [(define (is-wider? i)
				(> (image-width i) w))]
			(filter is-wider? loi)))

Because filter can only accept a function that itself only takes a single argument, w is not passed to is-wider?. In other words, the w must come from the enclosing function and it cannot be defined at top level. The reason this works is because of lexical scoping rules.