=====================================================
Generic Functions and the Zope Component Architecture
=====================================================

An interesting area of Computer Science and Software Engineering is the topic
of `Generic Functions`. In this system you can define many functions with the
same name, and have the correct one 'chosen' based upon the types of the
arguments that you supply to it. For example, consider a group of functions
that serialize objects based upon the object type::

    >>> def datetime_serializer(dt):
    ...     ...   
    >>> def int_serializer(integer):
    ...     ...
    >>> def string_serializer(string):
    ...     ...

Each of these functions do essentially the same thing: they serialize stuff,
but they vary as to the 'types' of their input paramters. With generic functions
however, you can supply three versions of a `serializer` function that differ
based upon their input types::

    >>> def serializer(dt):
    ...     ...
    >>> def serializer(int):
    ...     ...
    >>> def serializer(string):
    ...     ...

Crucially, the name of the function is identical in all cases, only the type
differs.  When `serializer` is actually called, the correct function object is
selected and invoked. This frees the caller from the need to know anything
about the object's type, pushing the concern into library code. Further, it may
be possible to supply additional `serializer` implementations in different
packages that broadens the application's overall functionality. 

What does this have to do with Zope?
====================================

Let's step back one moment and consider, in general terms, what is going on
here. Calling `serializer("hello")` results in a 'resolution' process that 
attempts to look up a function object based upon:

    a) a name; in this case `serializer` and
    b) the type signature, in this case `String`

Is this so dissimilar from Zope's adapter concept? With adapters, the Zope
component registry is given an interface and tuple of object types, and 
asked to supply an object that knows how to fullfil the interface's require-
ments from that type tuple. Here 'type' could mean an interface or a 
concrete class, or mixture of both. 

The calling code always spells out what kind of protocol it wants providing
via an interface. So, rephrasing the earlier example in Zope, we 
might like to formalise serialization as such::

    from zope.interface import Interface 

    class ISerializer(Interface):
        def serialize(obj):
            """
            Returns a string representation of the object provided
            """

Now, we can write a bunch of adapters that know how to actually fullfil 
this contract. As an example of one, consider::

    from zope.interface import implements
    from zope.component import adapts

    class ListSerializer(object):
        implements(ISerializer)
        adapts(list)

        def serialize(self):
            s = ""
            for item in self.context:
                s += item
            return s

We may also write such serializers that know how to work on `datetime` 
objects, dictionaries, and so on, and call them as such::

    >>> s = ISerializer([1,2,3]).serialize()
    >>> s = ISerializer("Hello World").serialize()
    >>> s = ISerializer(3.142).serialize()

Here, like with the generic function example, we always call the same thing, 
irrespective of the types of the parameters. Also like generic functions, we
could spread these defintions over multiple modules and packages, extending the
original implementations in new and interesting ways. 

In summary, with generic functions client code requests a serializer object
using the function name `serializer`, and gets back the result of the
serialization process as the return value. What about with adapters?  For this,
we use the function `getMultiAdapter` passing the interface and the objects we
want the adapter to adapt. The component registry investigates the types of
these objects, finds a satisfactory adapter for them (based on the interface),
and constructs an instance of it, passing the objects as parameters. This is
returned to the caller who may use the object (as descibed by its interface),
to perform some operation. In our example, the caller must call `.serialize()`
on the adapter to do the work. 

When we compare generic functions and Zope adatpers, we can see that there are 
two essential differences:

    a) Generic functions return the results of their work back to the 
       caller, whereas with adapters, the caller is handed the adapter
       back directly. Of course, generic functions can act like factories
       to return instances composed of its parameters. 
    b) A generic function states its intentions through the name of the
       function; in this case `serializer`, whereas adapters state
       their intention via *interfaces*. The name of the adapter is
       incidental and only of secondary concern, whereas generic functions
       are characterised by their names.

The gap between generic functions and adapters closes somewhat when we realise
that, in Zope, *functions* can be adapters too. A function doesn't need to
return an instance like a class does, so we can have it return the value
directly. This lifts the awkwardness of having to call `.serialize()` on the
adapter to do the work. Instead, the string representation of the object is
returned directly.

The syntax for this is quite simple::

    @adapter(list)
    @implmenter(ISerializer)
    def list_serializer(items):
        s = ""
        for item in items:
            s += str(item)
        return s

So now when we adapt a list, the serialized string is returned directly
to us::

    >>> ISerializer(["Hello ", "World])
    "Hello World"

We now have a situation whereby generic functions and adapters are doing pretty
much the same thing; dispatching to a function based upon the type signature of
the input. One difference remains however, Zope's adapters are using Interfaces
to signal the intent of the operation, whereas generic functions uses the
function name. 

Personally, I'd be content with this situation. Interfaces are much richer
contructs when compared to function names since they behave like proper types:
we can specify type hierarchies with interfaces using inheritance. Further, we
can attach narrative documentation to them which is placed in a central
location. Neither of these things are possible with generic functions.

But, "inquiring minds want to know". Can we actually simulate generic functions
in Zope?

Generic Functions with Zope
===========================

Having interfaces spell out the intentions of components is the bedrock
of the Zope Way (tm). When we register an adapter, we are specifying 
a mapping from an interface and collection of types, to a callable 
object::

    (Interface, (TypeA, TypeB, ...)) ----> CallableA

When we execute Interface(TypeA, TypeB), the component architecture indexes the
mapping, finding a matching interface/type-tuple pair, and returns
`CallableA()`. With a generic function implementation, we need to be able to
remove the interface dependency and map it with the *function name* instead::

    ('func_name', (TypeA, TypeB, ...)) ----> CallableA

Now, we'd be able to dispatch to the correct function based upon the
function name and type signature.

Although the Zope component interface *requires* you to use interfaces,
we can use named adapters to simulate the effect. Named adapters look 
like this::

    (Interface, 'some_name', (TypeA, TypeB, ...)) ----> CallableA

This extra name is the key to unlocking generic functions within the Zope
component architecture, since we can use the function's name in its string form
as the primary distinguisher. Of course, we still need to use interfaces as
part of the registration, but we can simply employ a marker interface that
denotes 'GenericFunctionish' things.  This results in a registration looking
like::

    (IGenericFunction, 'func_name', (TypeA, TypeB, ...)) 
                                                  ----> CallableA

The interface here has a subordinate role - it is the function name that is
defining the callable's *protocol*, rather than the interface.  This role
reversal is key to understanding how a generic function pattern would work with
Zope. In Zope parlance, we can describe generic functions as
'named-multiadapter factory functions'. 

The Implementation
==================

Turning our attention to an implementation, we would like to register
functions as named multiadapter factory functions on the fly. Then, when
the function is called, lookup an implementation using the Zope Component
Architecture.

It's pretty clear that decorators are made for scenarios such as this: "do some
magic to a function to register it, and then replace its implementation with a
registry lookup." We can envisage the system looking like this::

    @generic(list)
    def serializer(list_obj):
        ...

    @generic(int)
    def serializer(int_obj:
        ...

    @generic(datetime.datetime)
    def serializer(datetime):
        ...

Then we can call the `serializer` module-attribute to perform the registry
lookup and execute the desired function::

    >>> serializer(1)
    '1'
    >>> somemodule.serializer(2)
    '2'

The following is an implementation of such a decorator::

    def generic(*type_sig):
        def register(gen_func):
            name = gen_func.__name__

            def new_func(*args):
                obj = getMultiAdapter(args, IGenericFunction, name=name)
                return obj 

            implementer(IGenericFunction)(gen_func)
            adapter(type_sig)(gen_func)

            sm = getSiteManager()
            sm.registerAdapter(gen_func, type_sig, name=name)
            new_func.__name__ = name

            return new_func

        return register

In summary, this decorator takes the decorated function and registers it as an
adapter factory named as-per the function's name. All such generic functions
adapt to the interface IGenericFunction since they do generic function-ish
things. It then supplants the original function with one that performs a
component lookup; finding a function appropriate to the argument types, and
returning its result. Multiple definitions of the same functions are all
replaced with a single look-up function once the module has been interpreted.

Since this decorator is Zope-enabled, the function's type signature can also
involve Zope interfaces. Further, the registry already knows how to find the
correct implementation where competing parent types are registered for a given
subclass-instance. It will just do the Right Thing.

Conclusion
==========

Generic functions are advantageous when we are dealing with protocols with only
a single function, as in our `serializer` example. They remove the need to call
`.serializer()` and they are certainly more readible on face value, since they
are driven just like any other function invocation. However, this masks what is
a more complicated affair; the reader will only begin to understand what's
going on by looking at the function's definition - where she will discover the
decorator that reveals all. Conversely, the interface spelling is very much
more explicit and up-front. As an added bonus, looking up the interface
definition reveals narrative documentation.  You could argue that anyone
consuming both interface and generic function versions would need to read and
understand some accompanying documentation to understand it. In the case of
generic functions, this would have to make clear that the function
implementation resolved based on type. 

Another issue is that of scalability; if the `serializer` protocol is
elaborated to include a partner `deserializer`, then we have a dilema; do we
have separate `serializer` and `deserializer` functions, or do we change the
implementation to return instances with both methods (adapter style)? Having
separate function implementations is probably fine in this case, but as the
protocol grows, class adapters (containing multiple methods) come into their
own. They group the protocol under a single namesapce: for adapters, adding new
methods and attributes is toll free for client code, and so we trade off
simplicity for scalability. 

Both approaches are generally equivalent, and selection depends on your philos-
ophy towards interfaces. Either way, its satisfying that know that Zope has 
solutions for both. 
