Generational Interfaces

C

Carl Banks

While thinking about generational garbage collection, the thought of
generational interfaces occurred to me. I'd thought I'd run it by you
guys. I'm curious if there are any examples of this out there.

I've opined on this chat room before that interfaces are more often
cumbersome than helpful, especially in the early stages of a project
where lots of refactoring is (or ought to be) happening. But as
projects mature, interfaces do too, and that made me think interfaces
could be generated automatically by monitoring the software over time.

As an example, say I'm writing a flight simulator, and I have a
abstract base class Airplane, that I want to have an interface
someday, but I don't know what it is yet. So I define an

AirplaneInterface = InterfaceTracker("Airplane")

What this does is to open a sort of persistent database called
Airplane (details aren't important now). The database tracks all
objects that claim to implement the interface.

So say the first airplane is a Piper Cherokee. I'd write a class like
this:

class PiperCherokeeX1(object):
wingspan = 52.2
wingchord = 7.9
...
def __init__(self):
self.x = 0.0
self.y = 0.0
self.z = 0.0
...
set_up_initial_state()
...
AirplaneInterface.report(self)
def move_stick(self,dx,dy):
...
def move_thottle(self,ds):
...
def move_rudder_pedals(self,ddr):
...
def camera_matrix(self):
return self._quat_to_matrix(self.q0,self.q1,self.q2,self.q3)
def step(self,dt):
...

The key here is the call to AirplaneInterface.report() at the end of
__init__; this tells the interface tracker that, as of this call, this
object is implementing the Aircraft interface.

At this point, the interface tracker notes that PiperCherokeeX1 object
has certain methods (move_stick, move_throttle, etc), certain class
attributes, and certain instance attributes. And that's all it does--
at first. It just writes information into the database.

As time passes, and development continues, methods and data are added,
changed, reconfigured. For instance, I might split up move_stick()
into move_stick_x() and move_stick_y() for some reason. Then I might
get rid of these functions altogether in favor of a
move_control(self,n,dx). And so on. I add more classes that
implement the Aircraft interface, too. They look almost nothing like
the original interface.

However, through all that, the class attribute "wingspan" remains
there. Until one day when the project is quite mature I add a new
class, say Airbus380, that fails to define "wingspan". When this
class calls AirplaneInterface.report(), it raises an
InterfaceException.

Basically, the InterfaceTracker decides, after some threshold of
nearly universal usage of a certain method or attribute, that it has
become a required part of the interface and starts raising exceptions
when it's not there.

Make sense? Details can vary, but that's the basic idea. In this
way, you can combine some of the openness that helps in early
development, but also have some of the benefits of stricter typing
when things mature and turn out to be pretty strictly useful, without
much effort.

Thoughts? (Surely someone's thought to do this before.)


Carl Banks
 
P

Paul Rubin

Carl Banks said:
AirplaneInterface = InterfaceTracker("Airplane")
...
set_up_initial_state()
...
AirplaneInterface.report(self)
Thoughts? (Surely someone's thought to do this before.)

A decorator might express the idea a little more naturally.

Also, I'd say the interface tracker really should be told explicitly
when something is part of the interface and when it's specific to a
particular class implementing the interface. It shouldn't try to
guess this based on some operation appearing in more than one class.
Maybe there's some operation done on all jet planes and on no
propeller planes, that shouldn't be part of the top level airplane
interface.
 
C

Carl Banks

A decorator might express the idea a little more naturally.

Details. I didn't thoroughly map out the whole system in my head or
anything.

Also, I'd say the interface tracker really should be told explicitly
when something is part of the interface and when it's specific to a
particular class implementing the interface. It shouldn't try to
guess this based on some operation appearing in more than one class.

Well, that kind of defeats the purpose, doesn't it?

If you're going to all that trouble, it's probably not too much more
trouble to just manually specify the whole interface and be done with
it. Anyways you don't always know what's going to end up as part of
the interface in early stages.

It goes without saying there should be ways to manually override the
interface tracker's decision, but that falls more into the category
"So your interface tracker has guessed wrong".

Maybe there's some operation done on all jet planes and on no
propeller planes, that shouldn't be part of the top level airplane
interface.

Well, then the interface tracker would see that some member (say,
"set_mixture") exists on some objects and not others, and would
consider it not part of the interface. Only members that are defined
universally or nearly universally would be considered part of the
interface.

Now if you're concerned that you could implement a bunch of prop
planes, and then suddenly you throw a jet in there and it breaks
everything, there's a simple solution: convert the AirplaneInterface
to PropAirplaneInterface. (Yeah, suck it up and replace it
everywhere. If you haven't implemented any jets you shouldn't have
released the damn thing yet.) Create a new JetAirplaneInterface
tracker and (if useful) define Airplane as the union of the two.

Obviously something like this could get really intelligent--it could
perhaps work out the difference between props and jets itself (using,
say, statistical correlation). I bet there are some high-end code
analysis tools for various languages that do stuff like that. (But I
doubt many of them factor time into it; my idea was to incorporate
time into it somehow. Something does not become part of an interface
until it's universally used AND has been around for awhile.)


Carl Banks
 
P

Paddy

While thinking about generational garbage collection, the thought of
generational interfaces occurred to me. I'd thought I'd run it by you
guys. I'm curious if there are any examples of this out there.

I've opined on this chat room before that interfaces are more often
cumbersome than helpful, especially in the early stages of a project
where lots of refactoring is (or ought to be) happening. But as
projects mature, interfaces do too, and that made me think interfaces
could be generated automatically by monitoring the software over time.

As an example, say I'm writing a flight simulator, and I have a
abstract base class Airplane, that I want to have an interface
someday, but I don't know what it is yet. So I define an

AirplaneInterface = InterfaceTracker("Airplane")

What this does is to open a sort of persistent database called
Airplane (details aren't important now). The database tracks all
objects that claim to implement the interface.

So say the first airplane is a Piper Cherokee. I'd write a class like
this:

class PiperCherokeeX1(object):
wingspan = 52.2
wingchord = 7.9
...
def __init__(self):
self.x = 0.0
self.y = 0.0
self.z = 0.0
...
set_up_initial_state()
...
AirplaneInterface.report(self)
def move_stick(self,dx,dy):
...
def move_thottle(self,ds):
...
def move_rudder_pedals(self,ddr):
...
def camera_matrix(self):
return self._quat_to_matrix(self.q0,self.q1,self.q2,self.q3)
def step(self,dt):
...

The key here is the call to AirplaneInterface.report() at the end of
__init__; this tells the interface tracker that, as of this call, this
object is implementing the Aircraft interface.

At this point, the interface tracker notes that PiperCherokeeX1 object
has certain methods (move_stick, move_throttle, etc), certain class
attributes, and certain instance attributes. And that's all it does--
at first. It just writes information into the database.

As time passes, and development continues, methods and data are added,
changed, reconfigured. For instance, I might split up move_stick()
into move_stick_x() and move_stick_y() for some reason. Then I might
get rid of these functions altogether in favor of a
move_control(self,n,dx). And so on. I add more classes that
implement the Aircraft interface, too. They look almost nothing like
the original interface.

However, through all that, the class attribute "wingspan" remains
there. Until one day when the project is quite mature I add a new
class, say Airbus380, that fails to define "wingspan". When this
class calls AirplaneInterface.report(), it raises an
InterfaceException.

Basically, the InterfaceTracker decides, after some threshold of
nearly universal usage of a certain method or attribute, that it has
become a required part of the interface and starts raising exceptions
when it's not there.

Make sense? Details can vary, but that's the basic idea. In this
way, you can combine some of the openness that helps in early
development, but also have some of the benefits of stricter typing
when things mature and turn out to be pretty strictly useful, without
much effort.

Thoughts? (Surely someone's thought to do this before.)

Carl Banks

I thought a major use of an interface is to allow separate development
that comes together at the interface. If so then such fluid interface
changing would scupper separate development.

- Paddy.
 
C

Carl Banks

I thought a major use of an interface is to allow separate development
that comes together at the interface. If so then such fluid interface
changing would scupper separate development.


Yes, this wouldn't be appropriate for that particular use.


Carl Banks
 
B

bearophileHUGS

I think they can be called "soft interfaces" or "heuristic
interfaces", it's an example of machine learning. I think it's not
easy to apply such idea to a statically typed language, but probably
it can be done on Java. I presume in the future GUI will learn more
about the usage patterns of their users, data structures of long-
running programs will adapt to their average usage in each specific
point of the program, and maybe class interfaces too will become
smarter, as you say :)
I think your idea can be implemented in Python too, so you just have
to try to use it in some project of yours developed with some Agile
programming style, so you can tell us if it's nice to use :)

Bye,
bearophile
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top