Code and data

2013-11-03

Reading through SICP I have been thinking a lot about abstraction as well as the 'code as data and data as code' mindset.

A quick note for those not too familiar with lisp-syntax; when I say (operation arg1 arg2 arg3) simply pretend I am saying operation(arg1, arg2, arg3).


Typically I think of procedures (code) as the things that operate on data and data as the thing we give code to work on; that is that the code is the do-er and is active, where as data is passive.

This distinction starts to break down once we are able to pass around procedures as values.


Consider a simple object representing a bank balance (written in scheme)

;; constructor for an object which represents a bank account
;; supports "withdraw", "deposit" and "balance" operations
(define mk-account
  (lambda (balance)

    ;; deposit into account
    (define deposit
      (lambda (amt)
        (set! balance (+ balance amt))))

    ;; withdraw from account
    (define withdraw
      (lambda (amt)
        (set! balance (- balance amt))))

    ;; query account balance
    (define get-balance
      (lambda ()
        balance))

    ;; provide an object-like interface
    ;; takes an argument (`message`) which determines
    ;; which procedure we return to the caller
    ;; the caller then calls this procedure to perform
    ;; tasks.
    (define dispatch
      (lambda (op)
        (if (equal? op "withdraw")
          withdraw
          (if (equal? op "deposit")
            deposit
            get-balance))))
    dispatch))

This provided quite a simple interface, at this point we still have the typical code-data distinction.

;; construct new account with initial balance of 1000
(define my-account (mk-account 1000))

;; withdraw 200
((my-account "withdraw") 200)
;; deposit 75
((my-account "deposit") 75)
;; print balance => 875
(display ((my-account "balance"))) ;; => 875

Notice that at this point we still have the form of (procedure data...), the only difference is that we have intermediary procedure calls ((procedure data...) data...)


We can of course abstract this further; and at this point the distinction begins to blur.

;; take an account and return procedure
;; for querying balance
(define get-balance
  (lambda (acc)
    (acc "balance")))

;; take an account and return procedure
;; for withdrawing from account
(define withdraw
  (lambda (acc)
    (acc "withdraw")))

;; take an account and return procedure
;; for depositing into account
(define deposit
  (lambda (acc)
    (acc "deposit")))

and the usage becomes

;; construct a new account
(define my-account (mk-account 1000))

;; perform a withdrawel and deposit
((withdraw my-account) 200)
((deposit my-account) 75)
;; print balance
(display ((get-balance my-account))) ;; => 875

notice that we now have (withdraw my-account); both withdraw and my-account are procedures, we now have two equivalent ways to express the same idea

(my-account "withdraw") ;; (procedure data)
(withdraw my-account)   ;; (procedure procedure)

But the withdraw procedure is purely syntactic sugar for the first form, so we can also think of this as

(withdraw my-account) ;; (data procedure)


We still have the intermediary procedure call, we can of course trivially remove this.

(define get-balance
  (lambda (acc)
    ((acc "balance"))))

(define withdraw
  (lambda (acc amt)
    ((acc "withdraw") amt)))

(define deposit
  (lambda (acc amt)
    ((acc "deposit") amt)))

and the usage becomes

(define my-account (mk-account 1000))
(withdraw my-account 200)
(deposit my-account 75)
(display (get-balance my-account)) ;; => 875

now we have procedure calls of the form (withdraw my-account 200) which is (procedure procedure data) or possibly (data procedure data).


I find it very interesting that once we start to deal with higher order functions the typical data-code disinction not only breaks down, but becomes very muddled; in a single procedure call we can freely mix the two.



[1] Structure and Interpretation of Computer Programs http://mitpress.mit.edu/sicp/

[2] All examples in this post are written in scheme and were tested in my toy interpreter https://github.com/mkfifo/plot