[PEAK] PeakPlace, Junction
Phillip J. Eby
pje at telecommunity.com
Sun Dec 7 22:00:07 EST 2003
At 05:59 PM 12/7/03 -0800, John Landahl wrote:
>On Sun, 07 Dec 2003 19:32:11 -0500, Phillip J. Eby <pje at telecommunity.com>
>wrote:
>>Part of my point is that it means a Junction is not a reusable component;
>>you can't e.g. implement multiple junctions in one process with different
>>configurations.
>
>That's a possibility that I hadn't considered. My first reaction was to
>say that you wouldn't want to do that, but on second thought it opens up
>some really interesting possibilities for much larger applications.
Mutable (aka stateful) singletons are evil. Really, truly, evil. (Because
how many of a thing you have is a policy decision, not a mechanism.) About
the only thing I really actually dislike about twisted is its insistence on
the use of singletons, especially the reactor. Thus, my first reaction to
a singleton is always to question it. Usually, this does open up
interesting possibilities, although not necessarily from the first moment.
Anyway, there's a very good reason why 'binding.Component' and
'binding.Singleton' in PEAK don't mix. :)
>>* Never instantiate a component as the default value of a binding.Make;
>>this will not work correctly if there is more than one instance of your
>>class. (If there really is only one instance, make it a singleton; but
>>that's usually only sensible if it has no state.)
>
>Where did you see this happening?
The junction class has two Obtain() calls that have default=someObj(). I
misinterpreted them as component instantiations, (and mistakenly called
them Make calls) -- sorry. If it were me, I'd make the ServiceLocator a
binding.Singleton (since it's stateless), so there'd be no need to
instantiate it. And I'd set a "constant" to dnsUUID() near the top of the
module, then refer to it in the default. Thus, I wouldn't be distracted by
the apparent creation of components in the defaults. (Heck, I'd probably
export a defaultUUID from the util module, and then import that for use as
the default.)
Of course, I'm probably just paranoid and superstitious, here. But I
definitely wouldn't want to give somebody else the impression that
constructing a possibly mutable object as the default for an Obtain()
binding is a good idea.
Hm. I wonder if it would be useful to allow configuration keys to be
unioned? That is, if you could do, e.g.:
thing = binding.Obtain( IServiceLocator |
"import:junction.util.ServiceLocator" )
That is, first try to find an IServiceLocator, then fall back to importing
the default. Interesting.
In practice, it would probably need to be a class or function, like
'config.OneOf(IServiceLocator, "import:...")', because most of the types
used for configuration or component keys aren't going to support the right
meaning of '|'. But it might get rid of the need to even *have* a default
value for Obtain, if coupled with say a 'config.default'.
I imagine it would be a good idea for me to think some more about this sort
of component key, config key, or recipe "arithmetic". There do seem to be
some common patterns floating about.
One other thought that's been on my mind lately is whether PEAK really
needs to separate the 'binding' package into 'binding' for attributes, and
a separate 'components' package for the component/hierarchy stuff. There
are probably also a couple of config items that might move to 'components'
in that case.
Here's why. For explanatory purposes, I talk about "bindings" a lot, in
ways that imply they are only attributes (Make, Obtain, Require,
Delegate). But, there are a bunch of other binding API's that are really
*component* APIs. Like lookupComponent, IComponentKey, and so on.
This is probably going to annoy the folks who just asked for a 'core' API,
since now there'd be two things being imported where one previously
sufficed. As I see it, we'd end up with:
binding: Make, Obtain, Require, Delegate, classAttr, IActiveDescriptor,
IBindableAttrs, IRecipe (plus getInheritedRegistries, supertype,
Descriptor, Attribute, AttributeClass, Activator, and ActiveClass)
components: lookupComponent, getParentComponent, getComponentName,
suggestParentComponent, getComponentPath, getRootComponent,
notifyUponAssembly, Singleton, Component, ComponentName, IAttachable,
IBindingNode (needs a new name), IComponent, IComponentFactory, IComponentKey,
During a transition period, the 'components' API would also be available
under the 'binding' namespace.
Now that I've actually proposed it, I do see some problems with it. My
main intent is to group things in chunks that allow sensible
explanation. One of my big problems trying to write the original tutorial
is that the ideas contained under the heading of 'binding' were extremely
broad. Still, I'm not sure the split would help that much, because the
concepts *are* rather intermingled. Certainly the implementation of both
packages would still be deeply tangled. Hm.
I suppose the thing that I really want to accomplish is find a way to add
convenient utilities like the 'oneOf' thing, and not have them confused
with actual bindings like Make and Obtain. Thus, one could Obtain(
oneOf(...) ) and have it be clear what was going on.
Sometimes, it seems to me that there is an even simpler concept inside of
the binding and naming packages, that's still struggling to get
out. Probably it's just that I want recipes and component keys and all of
those things to be composable, via things like the oneOf() idea or the
current naming.Indirect(). Using lambda: or functions to do things in
bindings nearly always seems klunky to me, when they're things that you do
over and over again.
Ah well. Probably I'll just go through in a4 and re-sort where the code
lives in the binding package, so that everything's in reasonable
places. Maybe make 'adapters' modules in binding and config to hold their
respective adapter classes, just like there are 'interfaces' modules for
interfaces.
Anyway, this is all rambling and largely unrelated to your post, so I'll
stop now. :)
More information about the PEAK
mailing list