10.3. More Features

10.3.1. Objects

Standard Scheme has no concept of data structures (or even classes) as we know it from "traditional" programming languages. The only built-in data structures are the collection types pair, list (being a special case of pair), and vector. Nonetheless, there are surprisingly simple ways to implement abstract data types in Scheme.

The starting point is the observation that each function has its own frame which contains the local variables defined in the function. We have also seen, that functions can return other functions which keep references to the frame of the original function. Combining these two elements, we can use the frame of a function as a structure holding the state of an object. The "trick" is to return a method dispatcher function.

> (define (make-account balance)
    (define (withdraw amount)
      (set! balance (- balance amount))
      balance)
    (define (deposit amount)
      (set! balance (+ balance amount))
      balance)
    (define (dispatch method)
      (cond ((eq? method 'withdraw) withdraw)
            ((eq? method 'deposit) deposit)))
    dispatch)
> (define account (make-account 100))
> ((account 'deposit) 25)
125
> ((account 'withdraw) 50)
75

The initial balance is stored in the local frame of the constructor function make-account. Within this function we define two functions, the "methods" withdraw and deposit, which manipulate the balance. Next we define the method dispatcher function dispatch which just return the method function associated with the given method symbol. This method dispatcher is return as our account "object". The state of the object is contained in the frame continues to exist, since it is used by the returned function.

We can improve the method call syntax by not returning the dispatch function itself, but a function applying it to the given arguments.

> (define (make-account balance)
    (define (withdraw amount)
      (set! balance (- balance amount))
      balance)
    (define (deposit amount)
      (set! balance (+ balance amount))
      balance)
    (define (dispatch method)
      (cond ((eq? method 'withdraw) withdraw)
            ((eq? method 'deposit) deposit)))
    (lambda (method . args) (apply (dispatch method) args)))
> (define account (make-account 100))
> (account 'deposit 25)
125
> (account 'withdraw 50)
75