Objects & Closures
Published at Jun 21, 2025
“Master, I have diligently studied the matter, and now understand that objects are truly a poor man’s closures.” Qc Na responded by hitting Anton with his stick, saying “When will you learn? Closures are a poor man’s object.” At that moment, Anton became enlightened. — Anton van Straaten
Closure is a fundamental programming concept and I find being able to express the essence of objects and class through it incredibly satisfying. In this article , I would like us to explore this idea leveraging power of functional programming and macros, using a Lisp dialect, Racket.
Here is a simple Point class from the MDN documentation, that I’ve tweaked a bit for exploration purpose.
class Point{
constructor(x,y){
this.x = x;
this.y = y;
}
static displayName = 'Point';
static distance(a,b){
const dx = a.x - b.x;
const dy = a.y - b.y;
const squaredSum = (dx * dx) + (dy * dy);
return squaredSum ** 0.5;
}
getX(){
return this.x
}
getY(){
return this.y
}
translateX(offset) {
this.x += offset;
}
}
Our goal is to build something similar to the MDN example using only functions and closures. Let’s begin by creating a point function. It will take x and y coordinates and return a new function that captures them in its closure. Let’s ignore the statics for now.
;; point takes in x and y
;; and returns a function
;; with x and y in closure
(define point
(lambda (x y)
(lambda (m)
(case m
[(getX) (lambda () x)]
[(getY) (lambda () y)]
[(translateX) (lambda (offset) (+ x offset))]))))
To create a point “object”, we would simply call the function with arguments.
(define p1 (point 1 2))
;; => p1 (our object) is now a function that accepts a method name as a symbol
Now, to invoke getX, would for now be a little unconventional but it can be done.
;; (p1 'getX) returns the getX lambda
(p1 'getX)
;; to actually invoke the getX, we would
;; need to call the return value once more
((p1 'getX))
;; => 1
((p1 'translateX) 2)
;; => 3 (offsets 1 by 2)
It would be simpler if we could invoke the method using syntax like (apply p1 getX) . Luckily, Racket’s macros provides a simple way for us to create our own syntax.
;; It would expand to what what we did
;; while calling methods on p1
(define-syntax apply
(syntax-rules ()
[(apply o mn args ...)
((o 'mn) args ...)]))
;; A cleaner syntax
(apply p1 getX)
;; => 1
;; ((p1 'translateX) 2) becomes: (apply p2 translateX 2)
(apply p1 translateX 2)
;; => 3
Now, going back to those statics, it becomes clear that our current implementation only gives us a way to represent points and not classes. This is a very subtle distinction.
The static properties, displayName and distance, belong to the Point class itself, not to any single point object. Its not terribly difficult to extend our example to have static properties, we just apply another layer of closure.
;; an initial attempt at something like a class that
;; gives us point object
(define (make-point)
(let ([displayName "Point"]
[distance (lambda (a b)
(let* ([dx (- (apply a getX)
(apply b getX))]
[dy (- (apply a getY)
(apply b getY))])
(sqrt (+ (* dx dx)
(* dy dy)))))])
;; The Class Dispatcher
(lambda (cm)
(case cm
[(displayName) displayName]
[(distance) distance]
[(new)
(lambda (x y) ;; <= what we used before, our object creator
(lambda (m)
(case m
[(getX) (lambda () x)]
[(getY) (lambda () y)]
[(translateX) (lambda (offset) (+ x offset))])))]))))
;; `Point` is our "class"
(define Point (make-point))
;; making the point objects
(define p3 ((Point 'new) 1 2))
(define p4 ((Point 'new) 5 6))
;; calling the static method distance
((Point 'distance) p3 p4)
We have successfully modeled a single Point class, but being able to generalize it is would be more interesting . Using Racket’s macros, we can create a syntax similar to MDN javascript example do define any class.
;; This macro just allows writing
;; syntax for creating object
(define-syntax object
(syntax-rules (fields methods)
[(_ (fields [fn iv] ...)
(methods [mn imp] ...))
(let ([fn iv]
...)
(lambda (m)
(case m
[(mn) imp]
...)))]))
;; Similar to previous example
;; just using macro that lets us
;; create class
(define-syntax class
(syntax-rules (fields methods)
[(_ name
(statics [stn is] ...)
(fields [fn iv] ...)
(methods [mn imp] ...))
(define name
(let ([stn is] ...)
(lambda (cm)
(case cm
[(new)
(object (fields [fn iv] ...)
(methods [mn imp] ...))]
[else cm]))))]))
Now, defining a new class is so much more clear.
(class BetterPoint
(statics [displayName "Better Pointer"])
(fields [x 0]
[y 0])
(methods [setX (lambda (v) (set! x v))]
[setY (lambda (v) (set! y v))]
[getX (lambda () x)]
[getY (lambda () y)]))
;; Calling static methods
(BetterPoint 'displayName)
;; A simple object
(define betterPoint1 (BetterPoint 'new))
(apply betterPoint1 getX) ;; should give 1
;; Mutating state
(apply betterPoint1 setX 2)
(apply betterPoint1 getX) ;; should give 2
And with that, we’ve come remarkably close to the original JavaScript class example. For me, this is simply amazing: the combined power of functions, closures, and macros.
The full source code for this exploration can be found on GitHub.