[Prev][Next][Index][Thread]
Re: Dylan & object/relational mapping
I have a couple of comments regarding your quest...
First, the fact that Dylan doesn't allow you to use static syntax to
refer to dynamic data is not necessarily a bad thing. It's good
software engineering. If Dylan allowed your program to say x.Foo
without defining or importing Foo, then neither the compiler nor
future readers and maintainers of your code would have a clue as to
whether you, the author, expected that Foo will be an accessor defined
at runtime by a database, or whether you meant to type x.Fou and just
made a mistake. This sort of thing might be useful during
development, and it might be worthwhile to think about how the
Interactor could help support that kind of experimentation, but it
really doesn't belong in production code. It does not in any way
diminish the abstraction of your code to have to say somewhere 'define
database-accessor Foo;' or something to that effect, if you really
want to use static (compile-time) accessors.
I'll just mention that the product formerly known as Harlequin Dylan
internally has a project tool plug-in architecture that in theory
would allow you do generate these accessor definitions at compile
time, by reading the database schema at compile time. This is similar
to how CORBA IDL and COM/OLE type libraries are handled.
But I think the most natural way in Dylan to express the situation you
describe is with dynamic symbolic accessors. I.e. rather than saying
x.Foo, you would say x[#"foo"]. This is explicitly saying that you
are looking at a dynamic (run-time) lookup rather than static
(compile-time) lookup. The way you get to use the table syntax is by
defining an 'element' method:
define method element (x :: <eo>, what :: <symbol>, #key default)
get-value(x, what)
end;
and similarly for element-setter.
Using this symbolic syntax does not prevent you from using the full
power of the object system, and it does not prevent you from
custom-coding accessors. That's because Dylan has (1) multi-methods
and (2) symbolic singleton specializers.
Thus if you have an accessor #"velocity" that is normally stored in
the database but now you decide to compute it, you would just define a
new method:
define method get-value (x :: <eo>, what == #"velocity")
x[#"distance"] / x[#"time"]
end;
None of the callers have to change in any way. Similarly, if you
decide to cache the value in a static object slot, you just define a
new trampoline method:
define method get-value (x :: <eo>, what == #"velocity")
x.velocity
end;
The trampoline method could be generated automatically by a macro when
you define the slots of an EO class. If you seal them and provide
enough type information in the calls, the compiler can optimize them
out.
And of course you could have different kinds of implementations for
different kinds of classes. So for example, you could have a
<small-eo> which computed the velocity to save space, and <fast-eo>
which cached the velocity to speed access:
define method get-value (x :: <small-eo>, what == #"velocity")
x[#"distance"] / x[#"time"]
end;
define method get-value (x :: <fast-eo>, what == #"velocity")
x.velocity
end;
In none of these cases do the clients need to have any knowledge of
what's going on.
----
Gail Zacharias
gz -at- functionalobjects.com
Follow-Ups:
References: