----------------------------------------------------------------------

Today:

  Supporting operations on multiple types of data.
  called "Generic Operations" -- OPERATIONS X DATA TYPES

There are three basic techniques for supporting generic operations
  * Generic functions -- type dispatching, which we saw in the symbol. diff.
  * Message Passing
  * Data Directed Lookup

The first is supported by Dylan, and the one we will use most
during the term.  Probably also evolving as the most common method.

----------------------------------------------------------------------

Example:   Complex number arithmetic

  Complex numbers represented as ordered pairs:
   z = x+iy or equivalently z = mag * e^(i*ang) = mag cis ang

So, there are two natural representations:
  * Rectangular -- (x, y)
  * polar -- (mag, ang)

We could just choose one, but each one has its own strengths:

ADDITION:
 
  (x+iy) + (x'+iy') = (x+x') + i(y+y')  -- add real and imaginary parts

  It's not so easy in polar coords

MULTIPLICATION:

  (m cis a) * (m' cis a') = (m m') cis (a+a') -- multiply magnitudes
                                                 and add angles

  A bit harder in rectangular coords.

Thus we would like to have both forms:
  * Do we have to choose?

Suppose we had constructors
  (make-complex-polar mag ang)  --> z from mag and angle (polar)
  (make-complex-rect real imag) --> z from real and imag (rectangular)

These constructors could produce two different types of <complex> numbers, in
<polar> or <rect> form.  We know how to define <polar> and <rect> using
define-class with slots for the two parts of each number.

Then we would define GENERIC accessors that operate on either form:
  (real z) -- returns real part of *any* complex number.
    >>> Regardless of which way it was made
  (imag z) 
  (mag z)
  (ang z)

These accessors would simply use the underlying slot-ref or convert to the
right form as appropriate.  We can look at this in terms of the OPERATIONS x
TYPES:

[PUT THIS ON BOARD AND LEAVE UP:]

     TYPE:  <polar>    <rect>
OP:
----------------------------------------
 real	convert	      | slot-ref
----------------------------------------
 imag	convert	      | slot-ref
---------------------------------------- 
 mag    slot-ref      | convert
----------------------------------------
 ang	slot-ref      | convert
----------------------------------------


How would we define the types?  We want a <complex> type such
that <rect>'s and <polar>'s are both <complex>.  One way is to use the type
hierarchy (in the same way that <integer>'s and <float>'s are both <number>).

(define-class <complex> (<object>))

We refer to <complex> as an ABSTRACT TYPE, because it is not meant to be
instantiated --- don't do (make <complex>)

Some languages have support for declaring abstract types, so that they cannot
be instantiated.

Then we define the classes <rect> and <polar> as having the parent <complex>

(define-class <rect> (<complex>)
  (real <number>)
  (imag <number>))

(define-class <polar> (<complex>)
  (mag <number>)
  (angle <number>))

[DRAW THE TYPE TREE]

With the standard way of constructing them using make (but named as make-complex):

(define (make-complex-polar <function>)
  (method ((mag <number>) (angle <number>))
    (make <polar> mag: mag angle: angle)))

(define (make-complex-rect <function>)
  (method ((real <number>) (imag <number>))
    (make <rect> real: real imag: imag)))

Given the generic operators -- which we'll have to build -- we can
then implement complex number arithmetic quite easily:

(define (mul-complex <function>)
  (method ((x <complex>) (y <complex>))
    (make-complex-polar
     (* (mag x) (mag y))
     (+ (angle x) (angle y)))))

(define (add-complex <function>)
  (method ((x <complex>) (y <complex>))
    (make-complex-rect
     (+ (real x) (real y))
     (+ (imag x) (imag y)))))

Subtraction and division are similar.

** So, we've built complex arithmetic on top of those constructors and
   selectors 

What is the abstraction boundary?  REAL, IMAG, MAG, ANGLE, and the
constructors MAKE-COMPLEX-POLAR and MAKE-COMPLEX-RECT

----------------------------------------------------------------------

(Note: We *could* just implement complex numbers in terms of, say,
rectangular coords, and convert to and from polar whenever we need to,
but that doesn't let us automatically use whatever is most efficient
which we could do with both representations coexisting.)

Given that we want both types to co-exist, we now want to define GENERIC
operations REAL, IMAG, MAG, and ANGLE which return the appropriate number
regardless of which type was given.

That is we would like selectors that operate on complex numbers
regardless of which form they are in. 

This can be done by defining generic functions, which are a collection
of functions that together share the same name.  We could simply
implement a function, REAL, that called the appropriate function
depending on the type of argument

In our table above, REAL corresponds to a row (the top row).

(define (real <function>)
  (method ((z <complex>))
    (cond ((instance? z <rect>) (get-slot real: z))
          ((instance? z <polar>) 
           (* (polar-mag z) (cos (as <float> (polar-angle z))))))))

AS does a coercion to the given type, e.g., makes sure the angle is a
<float> in this case.

This is a DISPATCH ON TYPE, which we saw in the symbolic
differentiation lecture, because the type of the argument
is used to determine the correct function to call (dispatch to the
right thing).

And analogously for IMAG, MAG and ANG

----------------------------------------------------------------

Dylan has a built-in mechanism to do type dispatching, known as 
generic functions.  A generic function is a collection of methods, where the
MOST APPROPRIATE method is invoked, given the particular type of argument.

A generic function is defined using the special form 

(define-generic-function NAME PARAMETERS)

which defines a generic, with the given name, where each method of the genric
takes the given list of parameters.

Methods (functions) are added to the generic using the special form

(add-method NAME METHOD)

where the given METHOD is added to the existing generic named NAME.

The generic function decides which of the specific functions (methods) to run
based on the types of the arguments passed to the generic, and the types of
the formal parameters of the specific functions. The "most appropriate"
function, given the types, is found and run.

The most appropriate function is defined as the one with the list of
formal parameters whose types most closely match the given argument
types.  In today's lecture this is simple, because there is only one function
whose parameter list matches a given argument, because no value
can be of both type <rect> and <polar>.

However, in general, with hierarchical types there could be several methods
whose types match a given argument list.  In such cases, the "more specific"
match is used, the type which is closest to the type of value. This will be
covered in more detail next time.

So for complex numbers:

(define-generic-function real ((z <complex>)))

(add-method real (method ((z <rect>)) (get-slot real: z)))

(add-method real (method ((z <polar>))
                   (* (polar-mag z) (cos (as <float> (polar-angle z))))))

Similarly for MAG,ANG, IMAG

Note: a generic cannot have more than one function with the same list
of parameter types!  Multiple definitions simply replace the previous one.

Generic functions do a dispatch on type, but it is done by the system
rather than explicitly by the programmer.  In a language that does not
support generic functions directly, it can be done using COND to do
the dispatch explicitly (as above).

Now if we define

(define (z1 <complex>) (make-complex-rect 1 2))

Then
(real z1) = ({real} {<rect> 1 2})
          = (get-slot real: {<rect> 1 2})
          = {1}

Aside:
  Operations like +, -, etc. in Dylan are GENERIC operations:
  --- they operate on many types of data 
  --- They sometimes do TYPE COERCION, converting from one type to
      another.  We will talk about this next time.
  --- can add new methods to them!

----------------------------------------------------------------------

A second way to implement generic operations is by MESSAGE PASSING:
  represent objects as entities that can accept messages about what
  operations to perform.

Every object knows how to handle various operations.
  * DISPATCH ON OPERATION  (rather than on type)

So the data is "active" -- it knows how to compute.  e.g., 7 is a
number that knows how to add itself to another number, multiply, etc...

Dylan does not support this directly, but we can implement it
using higher order procedures.

In MESSAGE PASSING,  each data type knows how to handle the
operations.  An object is told which operation to perform by
sending it a message.   

One way to implement this is using higher order procedures:

(define (make-complex-rect1 <function>)
  (method ((r <number>) (i <number>))
    (method ((op <symbol>))
      (cond ((= op real-part:) r)
            ((= op imag-part:) i)
            ((= op magnitude:) 
             (sqrt (+ (square r) (square i))))
            ((= op angle:) (atan i r))
            ((= op type:) rectangular:)
            (else: 
             (error "Unknown operation for rectangular complex number."))
         ))))

make-complex-polar is analogous, it also returns a function.

These are then used by "passing messages", e.g.,

(define z (make-complex-rect1 3 4))

(z imag-part:) ==> 4

Note that in this implementation all the complex numbers are
represented as objects of type <function>, because they are
implemented as functions.  Thus each object explicitly knows its type
(in response to the message type).


----------------------------------------------------------------------

Note that both techniques spend a *lot* on big cond clauses to figure
out what to do.  When there is direct programming language support, as here is
for generic functions in Dylan, then there is no explicit cond clause but in
effect it is still there.
  "Middle managers"

We can eliminate this paper-pushing administrative cost by 
DATA DIRECTED PROGRAMMING:

  * Make use of the following observation about generic operations:

2D table of type-specific actions indexed by data types and operations.
Just put the method's to do the work in the table!

  COLUMNS: dispatch on operation  (message-passing style)
  ROWS: dispatch on type 

This is a "Grand Unified View" -- look at the table as a combination
of type tag and operation, and forget the details of how the
dispatching is done.

This style of code is a bit awkward to read, so we won't go into it.
Things seem to be converging on the generic functions style, as it is
most like conventional functions/procedures.  You may run into the message
passing style though, as its used in languages like smalltalk.

----------------------------------------------------------------------

Sneak preview: Generic Operations and Coercion.

Extended Example: Generic Arithmetic System.

Generic operations are a technique for organizing (large) systems.
Means of managing complexity through abstraction.

Key ideas are:
  * Define 'abstract methods' that make sense on many types of objects
  * Hide the type-specific implementation from the user.

For example, you can ADD:
  - exact numbers ("integers")
  - rationals
  - inexact numbers ("reals")
  - complexes
  - polynomials
  - etc.
but the details of how you add them are quite different.

We have seen three ways of implementing generic operations:
  * Generic Functions (Dispatch on Type)
  * Message Passing (Dispatch on Operation)
  * Data Directed (table lookup)

The point of this was, 
  > Combination of data type and operation name tell you which
    procedure to use.
  > The three different ways were different ways of searching a "table
    of operations":

----------------------------------------------------------------------
Concept of the day: Generic Operations

Implementable in several ways:
  * generic functions (dispatching on type)
  * message passing (dispatching on operation)
  * data-directed programming (table of functions indexed by type 
			       and operation) 

Dylan supports generic functions directly in the language, with the special
form DEFINE-GENERIC-FUNCTION and with ADD-METHOD