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.