*very* revised proposal for interfaces

[Please excuse poor email format... I've just been moved to outlook and haven't yet figured out how to make it behave like a normal email program.] John, I really like your proposal!!! The main thing that I like about it is that, as you say: "It's also a lot easier to explain" That's usually a sign of a good design... when it all falls into place and becomes simple. I will admit, I've been trying to follow the various interface suggestions and have mostly been getting confused. This is simple and elegant, even if a few bits would improve with tweaking. Here is a running commentary on things I liked and disliked: 1. You create a new built-in type called "interface" with the special property that it does NOT appear in __bases__ even if declared to do so. I like this. Your syntax for the most common case of supporting an interface looks like this: class C(object, I): <... code here ...> If we DIDN'T create a "special" type (special because Python has to omit pure interfaces in generating __bases__), then things could still work by some sort of declaration after C is created: class C(object): <... code here ...> declareItImplements(C,I) But that's putting it in the wrong place. The declaration to support an interface should be easy to type and to read (in the common case), and should come at the TOP of the declaration of the class, not the end. So I like this. 2. You have a "magic class" __binding__. This sounds odd. Python hasn't ever used a "magic class" before. But it certainly makes the syntax simple. After all, you need to define a bunch of methods (the methods needed to conform to the interface). Declaring a class is the handy way to define a bunch of methods. So I think this is a little weird, but I don't really object. If there's another solution, though, I'd like to hear it. 3. Where does the "binding" live? Creating some sort of "global binding registry" is really not a good idea. Global is just bad, it becomes hard to find where things were set, which causes mysterious code failures, and so forth. At first, that's what I thought you were doing, but a closer look changed my mind. It seems to me that when you create a magic "__binding__" class what you are ACTUALLY doing is adding to a list of "classes implementing it" which is maintained in and associated with I, the interface object. That has ABSOLUTELY NO effect on anyone who is not using the interface, and it feels like the "right" place to store this information. So I if I understand this correctly, I like it. 4. You make use of the interface by invoking it on an object: i = I(d) This has been discussed before, and it's a good approach. It allows for interfaces to return the object (thus not creating extra objects unnecessarily) or a proxy (thus giving lots of extra flexibility). Solidly supported. 5. You do NOT provide a way to do type declarations along with the interface. Python isn't picky about types, it naturally supports a policy of allowing you to use any object that will do the job. That's a good thing for the language (even if my own preferences tend toward a stricter typing system) and interfaces aren't about undermining it... just strengthening it! So +1 here. 6. You do NOT provide a way to declare preconditions, postconditions, or anything like this for the methods. In fact, there's no specification other than a docstring. For this I'm not sure. I like the minimalistic, simple approach of just giving a docstring, but I might also like some of the power of extra features. If I have to give up extra power to get a simple, workable approach then I'll live with it. 7. There's no way to "extend" interfaces, since inheriting from an interface means something different. That's OK with me -- seems like something one would only do when in a severe meta-programming mood. So all in all, I'm liking what I'm seeing here. -- Michael Chermside

Chermside, Michael wrote:
John, I really like your proposal!!!
Thanks!
2. You have a "magic class" __binding__....So I think this is a little weird, but I don't really object...If there's another solution, though, I'd like to hear it.
That's pretty much my feeling, too. This is an area that could probably benefit from adding some language support, since I'm already pushing the class syntax pretty far. The idea for the magic class names came from the observation that class/interface bindings really are classes in themselves, but there's no need to ever refer to them directly, so they don't need real names.
3. Where does the "binding" live?
Creating some sort of "global binding registry" is really not a good idea. Global is just bad, it becomes hard to find where things were set, which causes mysterious code failures, and so forth. At first, that's what I thought you were doing, but a closer look changed my mind.
It seems to me that when you create a magic "__binding__" class what you are ACTUALLY doing is adding to a list of "classes implementing it" which is maintained in and associated with I, the interface object. That has ABSOLUTELY NO effect on anyone who is not using the interface, and it feels like the "right" place to store this information. So I if I understand this correctly, I like it.
Yes. It was pointed out to me privately that you can still make a mess if you have multiple bindings for the same class/interface pair (perhaps in a large project). My best answer to that concern so far is to say that (as a matter of style) bindings should always live with either the interface of the class they bind, and to enforce a rule that there may be only one explictit __binding__ per class/interface pair.
6. You do NOT provide a way to declare preconditions, postconditions, or anything like this for the methods. In fact, there's no specification other than a docstring.
For this I'm not sure. I like the minimalistic, simple approach of just giving a docstring, but I might also like some of the power of extra features. If I have to give up extra power to get a simple, workable approach then I'll live with it.
I'd really like to put more extensible error checking in, since it was in my original wish list. I left it out here because the proposal looks complete without it, and no solution I've come up with yet seems really satisfactory. I would welcome suggestions on how to do this well. As for syntax, I'm currently learning towards using magic methods named __pre__foo and __post__foo to check a method named foo. I'm kind of uneasy about extending special treatment from specific identifiers to *all* identifiers with a given prefix, but I suppose there is a precendent in the special treatment of module-level identifiers ("_" == no export) and class-level identifiers ("__" == pseudo-private). It's also fairly concise and expressive.
7. There's no way to "extend" interfaces, since inheriting from an interface means something different.
That's OK with me -- seems like something one would only do when in a severe meta-programming mood.
Actually you can create derived interfaces, if that's what you meant. If all of a class's bases are interfaces, the new class is an interface as well. I've seen subclassed interfaces put to very good use in the C++ STL, although the interfaces are just a documentation convention there. I'd be surprised if the Java standard libraries didn't contain a lot of dervied interfaces--I know the language allows it, even if it's not often used. Thanks (to everyone) for all the commentary. I feel like I'm making a lot of progress towards something that's functional and pleasant to use, but there are still a lot of open issues. I'll post a minimal reference implementation when I get done with it and hopefully generate some more interest in the proposal. I expect that when I start putting in more features there will be more cases where no solution is obviously the right one, so I'll need all the input I can get to avoid doing things in ways that seem backwards to 90% of my target audience. jw
participants (2)
-
Chermside, Michael
-
John Williams