A month ago I first announced the Reg library for Python. After posting it I got a comment asking why I didn't just use simplegeneric or PEP 443? One important reason is that I wasn't aware them. The other reason is that I want some special features. But it did get me thinking. Thanks commenter yesvee!
So what's PEP 443? It proposes functionality that lets you create generic functions that dispatch on their first argument (single dispatch):
from functools import singledispatch @singledispatch def fun(arg): return "The default" @fun.register(int) def int_fun(arg): return "Int argument" @fun.register(list) def list_fun(arg) return "List argument"
When you now call fun() it will dispatch to different implementations dependent on the type of the first argument:
>>> fun(1) "Int argument" >>> fun() "List argument" >>> fun('foo') "The default"
The advantage: dispatch on type, which is useful to extend classes from the outside, but just functions to the end user.
Reg is the Zope Component Architecture (ZCA) reimagined. The ZCA is interface-centric. Objects are looked up by interface, and an interface looks a lot like a class. Here's what it looked like with Reg (which has a simpler registration API):
import reg class IFunctionality(reg.Interface) pass def int_fun(arg): return "Int argument" def list_fun(arg) return "List argument" r = reg.Registry() r.register(IFunctionality, [int], int_fun) r.register(IFunctionality, [list], list_fun)
And then this is how you'd use it:
>>> IFunctionality.adapt(1) "Int argument" >>> IFunctionality.adapt("List argument") "List argument"
This API was in fact slightly less good as you could accomplish with the ZCA itself:
>>> IFunctionality(1) "Int argument" >>> IFunctionality("List argument") "List argument"
This is because I wanted to keep Reg simpler, and the ZCA has to do quite a lot of wizardy to make the latter functionality work. But I actually wanted it in Reg. And that starts to look suspiciously like a generic function implementation.
So I started thinking about rewriting Reg so it presented itself much like generic functions as in PEP 443, but with special Reg sauce added. I talked it over with a few people at PyCon DE too. Thanks for helping me clarify my thinking!
So last week I refactored Reg so be generic function based. Gone is lookup by interface, in is generic function calling. Here's what it looks like with Reg today:
import reg @reg.generic def fun(arg): return "The default" def int_fun(arg): return "Int argument" def list_fun(arg) return "List argument" r = reg.Registry() r.register(fun, [int], int_fun) r.register(fun, [list], list_fun)
And using it looks like a normal function call, just like in PEP 443:
>>> fun(1) "Int argument"
Reg doesn't deal with interfaces anymore. Interfaces tend to scare off Python developers as they're a new concept, and I realize they just weren't necessary anymore. Now people using your Reg-based APIs just use functions, like with any other API.
Why not use the decorator approach to register generic function implementations? Because Reg allows more than one registry. While writing this I realize that's not a good answer: I can provide this in Reg if I make the decorator a method on Registry. Stay tuned!
But anyway, Morepath, the web framework built on Reg that I'm working on, already supplies a decorator like this that integrates with its configuration engine.
So what's the special Reg sauce? Why not just use PEP 443? Reg offers a few facilities that I needed in Morepath:
Luckily you don't have to commit to Reg. You could implement your API using the more modest PEP 443 implementation first, and then switch to Reg should you discover its features would be helpful to you. You'd have to change how you register the implementations of generic functions, but not the API itself.
This should help you get started using Reg.
Reg is foundational to Morepath, the up and coming Python micro web framework with super powers. I've adjusted Morepath to use the new generic Reg. The morepath code is online. Stay tuned for docs!