On 2015-01-10 07:46, DaveG wrote:
Let me preface this by saying I only have a general conceptual
understanding of compilers and know nothing about actual implementation.
One common problem with Object-Relational Mapping (ORM) is what data to
load and when. There is basically 2 options:
1. Load everything: This certainly works, but is very inefficient,
particularly when you have a large number of fields or, even worse, have
a derived field that causes a join on one or more tables. If you need
all the data this is fine, but most of the time only a small subset is
actually used. (lazy loading can mitigate some issues, but introduces
other problems)
2. Specify what fields to populate: This can work, but makes more work
for the user, adds complexity to user code, and often introduces bugs
particularly over time as code changes. Implementation details are
leaking into the interface.
Basically, I'm looking for a way to "look ahead" to see what properties
on an object are actually referenced (or not referenced) so we know what
data needs to be loaded. Simple analysis for things like unused scope
variables already exist, but this is needed for properties on each
instance of a class (or struct). I guess this would require the compiler
to make 2 passes once to trace each object and a second to do something
with the data collected. This would potential cost a lot in compilation
time so there would probably need to be some sort of annotation on the
definition to indicate this type of check is necessary.
I might be crazy, but it seems like the compiler has all the information
necessary to figure this out and it would make user code simpler, less
error prone, and more efficient. So does anybody have any idea on how to
actually achieve this?
I'm not exactly sure if this is what you want but you can implement the
"opDispatch" [1] method in a class or struct. This method will be called
if no other method exists with the same name. There's also something
called "alias this" [2] that allows you to do something similar.
class Foo
{
void foo () {}
void opDispatch (string name)() {}
}
auto f = new Foo;
f.foo(); // will call "foo"
f.bar(); // will be lowered to f.opDispatch!("bar")();
If you're implementing an ORM I would recommend executing queries
lazily. You can do something like this:
class Person : ORM.Base
{
String name;
Int age;
// this method returns a range/proxy that implements the range api [3]
static ORM.Range!(Person) all () {}
}
"String" would look something like this:
struct String
{
alias get this;
// this method will fetch the data from the database
private string get ();
}
Using the interface would look something like this:
auto p = Person.all(); // no database query has been performed yet
// the range interface makes it possible to use a foreach
// when starting the foreach loop is when the first query will happen
foreach (e ; p)
{
// this call will trigger a call to the "get" method in "String"
// via the "alias this"
string name = e.name;
writeln(name);
}
[1] http://dlang.org/operatoroverloading.html#dispatch
[2] http://dlang.org/class.html#alias-this
[3] http://dlang.org/phobos/std_range.html#isInputRange
--
/Jacob Carlborg