Type Dispatching and Message Passing
Contents
SICP describes two ways by which we can call the functions associated with the given data: Dispatch by type, and Message passing.
These are just the ways (as is almost everything else in the book) of creating a new layer of abstraction on the top of what we already have. The use cases are different, but the end result is the same: call the methods associated with the data.
Dispatch by type is used in data-driven programming where the incoming data contains the necessary information of handling it. In this approach, we have a generic operation which needs to be applied to a given type. The type itself may be a generic type at a very high level of abstraction, but the data contains enough information to direct the operation to the necessary subtype. Therefore, this is a mapping from operation to type.
For example, given an operation like Add
and a generic type like Number
(which can be Real
or Complex
– the latter being an abstraction on two
integral/real numbers), the flow of dispatching by type will go to Number, then
to either Real or Complex, and if the number is Complex, then to it’s individual
parts.
In the book, this is done by tagging the data based on type. The upper layers of abstraction are used for bookkeeping and redirection, and the lower levels do the real work of handling data. As data moves from top to bottom, the tags are checked removed and the data is directed based on the value of tags; which are added again when the data needs to move up.
The other concept is Message Passing wherein we follow a 90° approach. Here we already have the type which provides a set of operations, and we pick the one we need and apply it to the data. Essentially we are sending a message to the data to apply a given operation.
The following code implements a table by using lists and exposing the functions which can work with the table.
We are creating an object in the function create-table
and pass back another
function dispatch
which receives the method name as a string, and returns the
unexposed function from the object. This function uses the parameters and do
whatever is asked of it.
|
|
Now using this code, we are going to implement both type-dispatch, and message passing. The following table helps in visualizing the connections between types (polar/rectangular) and functions (real-part, imag-part).
polar | rectangular | |
---|---|---|
real-part | # | # |
imaginary-part | # | # |
Type Dispatch
This means that we will have an operation which will be applied to the given
type, e.g. We are given real-part
which needs to be applied to polar
. In
some pseudo language (Haskellish), this will be the syntax of the operation:
|
|
Given the structure of the table above, we will have the data structured in the table like:
|
|
On the other hand, we have the message passing scheme, wherein we have the data
first, and the operation. It is up to the data to correctly apply the operation
and return the result. With respect to the table, we are moving from top to
bottom: polar.real-part
, polar.imag-part
, rect.real-part
, etc.
The implementation in scheme is:
|
|
Interestingly, the table itself implements the message-passing style since:
- Each table is a different object having its own copies of these methods (which should be shared when the code is running)
- We are passing each object the name of the operation to run.
Author Tushar Tyagi
LastMod May 8, 2016