Constructors

Chapter: Additional Features
...Section: Constructors

Now consider the problem of setting the interest rate for all accounts. For example we would like to be able to do the following:

> (define manager-account (Account 0000))
> (define wilma (Account 5678))
> (define fred (Account 6789))
> (call deposit wilma 87.00)
The balance is $87.00
> (call deposit fred 119.00)
The balance is $119.00
> (call add-interest manager-account 12345)
Interest added to all accounts.
> (call balance? wilma)
89.175
> (call balance? fred)
121.975

Notice that when we call the add-interest method on the object manager-account, interest is added to all accounts. We have made add-interest a class method, so that interest can be added to all accounts on the same day. The problem is, a class does not normally keep track of all instances of itself. To accomplish this, we create a static class variable called all-instances which is a list of all of the instances thus far created. We could maintain such a list without using static class variables, but it is more convenient to have this facility within our object system.

Our new class definition will begin something like this:

(define (Account)
  (class (user-pin)
    ([all-instances '()]
     [num-transactions 0]
     [vol-transactions 0]
           ....
     ) ... ))

We initially set all-instances to be the null list because no instances have been created. Each time we call the procedure Account to create an instance, we will cons that instance onto the static class variable all-instances. This variable will be shared by all classes.

Q. 9
How will we add each instance to the list? What would the code for this look like?


Again borrowing on the terminology of C++ and Java, we introduce the notion of constructors. Extending our Scheme object system with constructors will allow us to create and maintain a variable such as all-instances.

A constructor is a specific procedure which is performed each time a new object is created. Its main job is to initialize the internal state of the new object. In Scheme--, this is handled by the the let-like expressions in the class definition, in which object variables are declared and initialized using any expressions we want. However, in a system with static variables we may wish to update rather than initialize the value of such a variable each time a new object is created. A constructor may be used for other purposes as well, such as checking that the state created by the initialization of object variables is consistent and valid.

The new syntax for Scheme+- classes now allows optional statments to be added after the method definitions. These statements are executed each time a new object is created.

(define My-Class
  (class (... class parameters ...)
    ([cvar1 value1]    ;; static class variables
     [cvar2 value2]
         ...
     [cvarN valueN])    
    ([var1 value1]     ;; variables
     [var2 value2]
         ...
     [varN valueN])
    (Object)      ;; superclass
    ([meth1            ;; methods
       (method (a1 a2 ... aN)
         ... method body ...)]
     [methN
       (method (a1 a2 ... aN)
         ... method body ...)])
    constructor-stmt1
    constructor-stmt2
          ...
    constructor-stmtN))

This collection of trailing statments will function as a constructor. Note that there need not be any such statments, in which case the class definition will look just as it did before.

Now we demostrate a use of constructors for the bank account class.

(define Account
  (class (user-pin)
    ([all-instances '()]
     [num-transactions 0]           ;; class variables
     [vol-transactions 0]
     [interest-rate 0.025]
     [bank-code 12345]
     [output (lambda (a) (printf "The balance is $~s~%" a))])
    ([balance 0])                   ;; instance variables
    (Object)                   ;; superclass
    ([balance?                      ;; methods
       (method () balance)]
     
        ... deposit, withdraw, set-interest ...
	
     [add-interest
       (method (code)
	 (if (= code bank-code)
	     (for-each
	      (lambda (i)
		(call deposit i (* (call balance? i) interest-rate)))
	      all-instances)
	     (error 'add-interest "invalid bank access code")))])
    (set! all-instances (cons this all-instances))))

Notice that as usual the variable this refers to the current instance of the class, so in the constructor we are simply consing the new object onto our (static) list of classes.


Exercise 9

Copy the code you created in the previous exercise into a new file called account2.ss. Now add the following features to your bank account class:

Here is a sample transcript:

> (define manager-account (Account))
Your user pin is 8544.  Don't forget it!
> (define bugs (Account))
Your user pin is 6736.  Don't forget it!
> (define porky (Account))
Your user pin is 8782.  Don't forget it!
> (define elmer (Account))
Your user pin is 9251.  Don't forget it!
> (call deposit porky 1000)
The balance is $1000
> (call deposit elmer 900)
The balance is $900
> (call deposit bugs 2500)
The balance is $2500
> (call add-interest manager-account 12345)
The balance is $922.5
The balance is $1025.0
The balance is $2562.5
> (call change-pin bugs)
Enter your old pin > 6736
Enter your new pin > 1234
Enter it again > 1234
> (call withdraw bugs 100 6736)

Error in withdraw: check your pin or withdrawal amount.
Type (debug) to enter the debugger.
> (call withdraw bugs 100 1234)
The balance is $2462.5
> (call bank-change-pin bugs 6736 12345)
> (call withdraw bugs 100 1234)

Error in withdraw: check your pin or withdrawal amount.
Type (debug) to enter the debugger.
> (call withdraw bugs 100 6736)
The balance is $2362.5
> (call set-interest manager-account .25 12345)
> (call add-interest manager-account 12345)
The balance is $1153.125
The balance is $1281.25
The balance is $2953.125
> (call transaction-info manager-account 12345)
There have been 11 total transactions with a total volume of $5787.5
> (call change-pin porky)
Enter your old pin > 1111

Error in change-pin: Wrong old pin entered.
Type (debug) to enter the debugger.
> (call change-pin porky)
Enter your old pin > 8782
Enter your new pin > 1111
Enter it again > 2222

Error in change-pin: No match on retype.
Type (debug) to enter the debugger.





rhyspj@gwu.edu