[PEAK] fact orientation

Phillip J. Eby pje at telecommunity.com
Sat Oct 15 19:49:58 EDT 2005


At 11:31 AM 10/15/2005 -0700, Jay Parlar wrote:
> > It's a big part of it.  Dynamic variables, fact orientation, and monkey
> > typing are too...
>
>Interesting. I'm going to have to look into fact orientation, having
>never seen that term before. Do you have any good links to it? All the
>ones I find on Google seem pretty heavy with "market speak", I can't
>figure out what they really mean.

If you don't mind wading through some fairly deep reading, try this:

     http://www.fcoim.nl/Literature_Article_FCO.html

It's an elaboration on the fundamental principles of FCO-IM: Fully 
Communication-Oriented Information Modelling.

In brief, it basically states that the purpose of data modelling is to 
represent *human communication about the world*, NOT to model the world itself.

This is actually quite a radical and revolutionary idea with respect to 
software design as mostly currently practiced.  If you think through the 
ramifications, it means that object orientation as we currently conceive it 
is broken, because it's trying to solve the wrong problem.  Computers are 
useful for performing computation and queries regarding *communicable facts 
about objects*, which is not the same thing as representing *actual objects*.

In the FCO-IM view of the world, then, there exist only:

1. lexical types (symbolic values like numbers, strings, dates, etc.)
2. non-lexical "nominalized" types (conceptual entities, like a Person)
3. dimensional types (unit values like feet, seconds, mass, etc.)

What's more, the "nominalized" types do not exist as values.  You can't 
just refer to a Person, you have to say in effect, "The Person named Jay", 
or "The Person with SS#123-45-6789".

That doesn't mean you can't refer to a Person object in code, mind you, by 
abstracting out what that reference mode or key is.  I'm just pointing out 
that fact orientation doesn't try to model the *implementation of a 
Person*.  It models *facts about them* -- just like a relational 
database.  Indeed, I'd say the reason that relational databases are still 
with us, and object databases mostly haven't panned out, is for precisely 
the reason that relational databases are fact-oriented, and therefore more 
flexible and extensible in this way.

So, fact orientation is incredibly more flexible because you can always add 
more kinds of facts about a person, but in the OO paradigm a class is 
closed, with a fixed set of behaviors and characteristics.  If you combine 
generic functions (which can extend classes with new behaviors) with fact 
orientation (which can extend concepts with new kinds of facts), you have a 
completely open-ended system with regard to extensibility.

In short, present-day OO techniques are far less flexible than functional 
decomposition and relational logic are, despite the fact that OO is 
supposed to be their successor.  Nice, eh?  :)

But what OO *does* offer is programmer convenience, a hierarchical 
shorthand for expressing commonalities, and a more brief notation for 
obtaining or manipulating certain kinds of facts.  The problem with both 
fact orientation and generic functions in their "raw" form is that you end 
up with a giant global namespace and the need to use function syntax (ala 
Lisp) to get at anything.  Instead of saying 'somePerson.foo', you would 
have to say 'get_foo(somePerson)'.  And then, six libraries might have a 
'foo', so you really need 'somelibrary1.get_foo(somePerson)'.  Ugh.

So, this is where my "monkey typing" concept comes into play: mapping these 
more flexible models back into the syntax and patterns we know and 
love.  We simply use ISomeLibrary(somePerson).foo for setting, getting, 
deleting, and method calls.  These adapters are of course just a collection 
of descriptors that return bound methods wrapping the appropriate generic 
functions, or perhaps the results of calling them.

Ideally, monkey typing would be able to piggyback on Guido's proposal for 
implementing type declarations, so that it wouldn't be necessary to deal 
with these matters in-line most of the time.  Monkeytyping adapters are by 
nature safe for re-adaptation, in that switching to another interface just 
unwraps and re-wraps the underlying object.

Anyway, with my recent implementation of the schema.Annotation class for 
Chandler, I've realized how to do monkey-typing for fact-oriented systems 
using the same approach.  In Chandler, you can now do things like the 
following, which is a snippet from the schema API doctest:

     >>> class Teacher(schema.Annotation):
     ...     schema.kindInfo(annotates=Person)   # annotate the "Person" type
     ...     certifications = schema.Sequence()
     ...     supervisor = schema.One(Person)

     >>> class TeachingCertificate(schema.Item):
     ...     subject = schema.One(schema.Text)
     ...     certified_teachers = schema.Sequence(
     ...         Teacher, inverse=Teacher.certifications
     ...     )

     >>> ProfMary = Teacher(Mary)  # Adapt Person to Teacher
     >>> gym = TeachingCertificate("gym", subject=u"Physical Education")
     >>> ProfMary.certifications = [gym]
     >>> list(ProfMary.certifications)
     [<TeachingCertificate ... gym ...>]

     >>> list(gym.certified_teachers)
     [Mary Quite Contrary]

The extra state isn't stored in the adapters, though, it's part of the 
underlying database.  Which means you can adapt as many times as you like 
and still get the same data:

     >>> list(Teacher(Mary).certifications)
     [<TeachingCertificate ... gym ...>]

Thus, the data model for "Person" is *open ended*.  Any number of Chandler 
plugins can define their own additional data to be kept, and those 
additional attribute names don't clash with those defined by "Person" 
itself.  This gets Chandler out of the OO rut in a way that plain OODB's 
can't handle.  (Of course, I'm sort of faking it because Chandler is 
actually built on an OODB, not a relational one.  So in truth you could 
pull the same trick on top of other Python OODB's as well.)

Up until now, I'd always had this idea as a general concept of what I 
wanted to do with the "SOAR" project (Simple Objects Accessed 
Relationally).  But one of the conceptual stumbling blocks for me with SOAR 
was that I always got down to the problem of how to determine what a 
database object's "type" was, and how to determine whether it implemented a 
particular "data interface".  (If you look at the old TransWarp code for 
"records", you'll see a lot of this stuff there.)

But what I've realized from working with Chandler is - you don't *need* for 
objects to have a "type", in the sense that they can have one and only one 
type.  If you're stuck in the OO paradigm, it seems this way, because how 
else can you determine what method implementations will be used to respond 
to a particular message?  But in a facts+functions world, this is 
silly.  You just define things' behavior with generic functions, which are 
perfectly capable of determining behavior based on *whatever facts you'd 
like*.

So, when you view this in the context of "modelling human communication 
about things", you quickly realize that determining a thing's "type" is 
just a kludge.  Business applications are usually all about enforcing rules 
based on facts, anyway.  The "is-a" relationship is just another kind of 
fact, and doesn't require you to boil every object down to just one 
type.  In a sense, you can have multiple-inheritance on a *per instance* 
basis, if you like.

The problem this was causing with trying to implement polymorphic database 
schemas in PEAK and TransWarp was that we always had the implicit 
assumption that an object was always of just one type, and thus we needed 
to know what class to make the ghost be when we loaded an item.

But a fully fact-oriented model using generic functions doesn't have to 
care, because *the object itself has neither behavior nor data*.  The 
object is simply a key to access a collection of underlying facts.  Once 
you've realized that, then there's no "problem" to solve any more, except 
that you can't really go around using isinstance() on things unless you 
generate classes on-the-fly to match an individual object's behavioral 
signature.  (Which we could possibly do, but it seems easier to me to just 
deal with things in monkeytyping terms, with no "real" object.)

In Chandler, then, an individual object really *does* have a single type, 
but we use annotations to widen selected types with "third-party" 
attributes.  But in the monkey/facts/functions (MFF?) model, this won't be 
the case.  There will be no "real" objects at all, in the old sense, or if 
there are for efficiency's sake, it's just a coincidence.  All you'll ever 
see are "interface instances", never the "real object".

Doing this stuff in Chandler would be too much of a rework for no immediate 
gains, because Chandler doesn't have generic functions and the underlying 
storage mechanism is still married to the O-O model.  So I don't have any 
plans to push for implementing the full MFF model there.

For my own stuff, though, I'd like to be able to avoid there ever being 
"real" objects, but that may not exactly be how it works out.  There are 
still places where it's useful to have traditional, non-queriable objects 
in an application, but these are usually also the same objects for which a 
schema isn't really necessary or useful to begin with.

Which brings us to an interesting point: the primary usefulness of 
single-type, message-passing, closed class O-O is in creating 
*solution-domain* abstractions  like GUI toolkits, event frameworks, 
service components, etc.  That is, OO as we know it today is really a 
low-level toolkit useful mainly for solving programmers' problems, not 
users' problems.  :)




More information about the PEAK mailing list