This post is about building data abstractions in Clojure. The objective is to define abstract interfaces using only functions. We will not resort to mechanisms like protocols.
The technique we describe here is used in languages like Scheme, where functions are the only means of abstraction available.
As an example, consider the stack data type.
The basic interface for a stack consists of two functions -
pop. We can also add a third function for inspecting
the element at the
top of the stack.
The implementation of the interface assumes that an object providing the services of a stack should respond to the messages -
top. As we are strictly limiting our implementation to use only functions, the objects themselves will be
represented as functions that take these messages as keyword arguments. This leads to the following implementation of
the stack interface:
(defn push [stack item] ((stack :push) item)) (defn pop [stack] ((stack :pop))) (defn top [stack] ((stack :top)))
The actual implementation of a stack “object” that conforms to the above interface is shown next:
(defn make-stack ([items] (let [push (fn [item] (make-stack (cons item items))) pop (fn  (make-stack (rest items))) top (fn  (first items))] (fn [message] (case message :push push :pop pop :top top)))) ( (make-stack )))
make-stack function will return a function that can respond to the required messages. The stack itself is internally represented
as an immutable sequence.
The following session at the REPL shows that the immutable stack implementation can be used with the abstract interface that we defined earlier:
> (def s (-> (make-stack) (push 10) (push 20) (push 30))) > (top s) ;;=> 30 > (top (-> s (pop) (pop))) ;;=> 10
Here are a few ideas you can use to play around with our simple interface definition technique:
Exercise 1. Implement a mutable stack that can be used with the interface functions. Note that to be a stand-in replacement for the
immutable stack, the implementation of
pop must return the current stack “object” (i.e,
this in Java).
Exercise 2. Extend the interface with a function that reports the number of elements in a stack.
Exercise 3. Add a
visit function to the interface. It should take a single-parameter function that is applied to each element in the
stack in LIFO order and return a sequence of results.
Note that name and e-mail are required for posting comments