Object Orientation in Scheme
Guy Steele once said that ”A closure is an object that supports exactly one method: apply.”
When I read it a few days ago, I could not get much meaning out of it. While reading the SICP chapter 3, section 3.1.1, a bulb lit in my brain and I finally understood (atleast I think so) what he meant. I dug into other literature on the subject and found some more interesting stuff. Read on.
Let us consider the same example discussed by Abelson and Sussman in section 3.1.1, namely the withdrawal of money from a Bank account. One natural way of representing an account is to have an account object which has a way to represent the current balance. The following code reproduces it verbatim from SICP.
(define (make-account balance) (define (withdraw amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Insufficient funds")) (define (deposit amount) (set! balance (+ balance amount)) balance) (define (dispatch m) (cond ((eq? m 'withdraw) withdraw) ((eq? m 'deposit) deposit) (else (error "Unknown request -- MAKE-ACCOUNT" m)))) dispatch)
Let us analyze what is happening in the above code. The make-account procedure creates an “account” object, so to speak, which is initialized with the balance, as follows:
> (define acc (make-account 100)) > ((acc 'withdraw) 50) 50 > ((acc 'withdraw) 60) "Insufficient funds" > ((acc 'deposit) 40) 90 > ((acc 'withdraw) 60) 30
A call to make-account with an initial balance value creates a closure with the variable balance together with the internal routines: withdraw, deposit and dispatch. The value returned by make-account is the procedure dispatch. To call a particular method of the object, we pass it as a message to the closure. The returned procedure is then called with the arguments. This is rather the message passing style of object orientation. Now, how does this relate to the comment made by Guy Steele that “closure is nothing but an object with a single method - apply” ?
The above code can be re-written as follows:
(define (make-account balance) (define (withdraw amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Insufficient funds")) (define (deposit amount) (set! balance (+ balance amount)) balance) (define (dispatch m . args) (case m ((withdraw) (apply withdraw args)) ((deposit) (apply deposit args)) (else (error "Unknown request -- MAKE-ACCOUNT" m)))) dispatch)
All we have done is to use the case special form and use apply. The interactions with this new procedure is shown below.
> (define acc (make-account 100)) > (acc 'withdraw 20) 80 > (acc 'deposit 120) 200 > (acc 'deposit 120) 320 > (acc 'withdraw 40) 280
As we can see, this is a neat and easy way to create objects and explains Guy Steele’s statement regarding closures vs objects. In another blog post, we will see how to implement inheritance and other OO concepts. As you can see, Scheme is really cool and is an excellent platform for language experimentations and prototyping of ideas.