Now that we've finished our introduction to objects, it's worth
stepping back and asking what the relationship is (if any) between
functional and object-oriented programming.  After all, doesn't
everyone know that "real" programming is all object-oriented these
days?  So why bother learning functional programming?

It turns out that functional programming has a lot in common, and
therefore teaches you a lot about, object-oriented programming.  Let's
write the same code in both styles to illustrate this point.

Continuing our use of circles and graphics as an example, we now want
to create compound shapes (most drawing packages provide a way to
"glue together" existing shapes like this).  Since circles are our
only current shape, we'll call the compound shapes "blobs".

  A blob-object is either
    - a circle-object, or
    - a union-object which contains two blobs

Let's implement a simple service that counts how many circles are in
a blob-object.  Let's start doing this with objects.  How many classes
do we need?  We need circles, which we already have.  We also need
unions (do we also need blobs? -- more on that later in these notes).
Each of these classes needs to provide a count-circles service that
says how many circles are in that blob-object.

To keep things simple, I'm going to limit the circle-class to
providing just the count-circles service.  You could easily include
others based on previous lectures.

For circles, this is easy.  A circle has no other circles than itself,
so the service just returns 1.

(define make-circle-obj
  (lambda (initrad)
    (local [(define (count-circles) 1)]
      (lambda (service)
	(cond [(symbol=? service 'count-circles) count-circles])))))

What would our union-object class look like?  Since a union object
contains two blob-objects, it has to take those as initialization
parameters: 

(define make-union-obj
  (lambda (blob1 blob2)
    (local [(define (count-circles) ...)]
      (lambda (service)
	(cond [(symbol=? service 'count-circles) count-circles])))))

What does the body of count-circles need to do?  Since blob objects
are all supposed to provide a count-circles service, we can simply
count the circles in each blob and add the results:
	   
(define make-union-obj
  (lambda (blob1 blob2)
    (local [(define (count-circles)
	      (+ ((blob1 'count-circles))
		 ((blob2 'count-circles))))]
      (lambda (service)
	(cond [(symbol=? service 'count-circles) count-circles])))))

How might we use this code?

> (define b1 (make-union-obj
              (make-circle-obj 4)
              (make-union-obj 
               (make-circle-obj 2)
               (make-circle-obj 8))))
> ((b1 'count-circles))
3

Now let's write the same code functionally, as we would have before
the midterm.

  A blob is either
    - (make-circle number)
    - (make-union blob blob)

  (define-struct circle (radius))
  (define-struct union (blob1 blob2))

;; count-circles : blob -> number
;; counts the circles in a blob
(define (count-circles ablob)
  (cond [(circle? ablob) 1]
	[(union? ablob)
	 (+ (count-circles (union-blob1 ablob))
	    (count-circles (union-blob2 ablob)))]))

> (define b1 (make-union
	      (make-circle 4)
	      (make-union
	       (make-circle 2)
	       (make-circle 8))))
> (count-circles b1)
3

What are the similarities and differences between the two versions?

- Both return 1 if the object is a circle and the sum of recursive
  calls if the object is a union.

- The functional version uses a cond to tell whether the blob is a
  circle or a union.  The object-oriented version didn't need to do
  that because each object has its own version of count-circles.  This
  is one of the powers of OO programming: the language automatically
  calls the right version of count-circles, without the need for the
  cond.  This feature is called "dispatching" in OO languages.

- The functional version needed two cases in the cond, while the OO
  version needed two classes.

None of these are coincidences.  OO programming is like functional
programming flipped on its side.  To turn a functional program into an
OO program, you

- create a class for each line in the data definition for the
  functional program,

- add a service to each class with the same name as the original
  function (from the functional program),

- make the body of the function in the service the same as the
  answer in the corresponding cond clause in the functional program (1
  goes to circle class, the sum goes to the union class), and

- slightly fix the syntax (since recursive calls on functions are a
  little different looking than recursive calls on objects -- for
  example, (count-circles blob1) versus ((blob1 'count-circles)).

That's it!  The LOGIC of the two programs is exactly the same.  The
code is just organized a little differently.  

So, what's the moral?  Functional programming teaches you how to
program well with objects!  Functional programming teaches you, at the
simplest level, how to program with recursively-defined data (such as
lists and trees).  Programs over recursively-defined data must be
recursive, even if you write those programs in an OO language.

But wait -- some of you have OO programming experience and programmed
with lists but without recursion (using loops).  So how can what I
said be true?  Loops _are_ a form of recursion.  They're just
customized to working with certain kinds of data.  They don't scale
though (try writing a program over trees with just loops, and without
using goto and break statements).  Functional programming shows you
how to program scalably with recursively-defined data, and this
lecture shows that these lessons apply equally well to object-oriented
programming. 

We still have a lingering question from above though -- do we need a
class for blobs as well as for circles and unions?  We didn't need it
for the specific example here, but when you move to full-fledged OO
programming, you would create a blob class as well, with the circle
and union classes inheriting from the blob class.  The blob class
would provide a place for common code between the circle and union
classes.