DeRailed DSL (was Re: compile-time regex redux)
Andrei Alexandrescu (See Website For Email)
SeeWebsiteForEmail at erdani.org
Sat Feb 10 19:29:42 PST 2007
Kirk McDonald wrote:
> Bill Baxter wrote:
>> Speaking of which I'm surprised Kirk hasn't piped in here more about
>> how this could make life easier for PyD (or not if that's the case).
>> Any thoughts, Kirk? You're in one of the best positions to say what's
>> a bottleneck with the current state of compile-time reflection.
>>
>> --bb
>
> One area of Pyd which I am unhappy with is its support for inheritance
> and polymorphic behavior.
>
> http://pyd.dsource.org/inherit.html
Great lib, and a good place to figure how introspection can help.
> Getting the most proper behavior requires a bit of a workaround. For
> every class that a user wishes to expose to Python, they must write a
> "wrapper" class, and then expose both the wrapper and the original class
> to Python. The basic idea is so that you can subclass D classes with
> Python classes and then get D code to polymorphically call the methods
> of the Python class:
>
> // D class
> class Foo {
> void bar() { writefln("Foo.bar"); }
> }
>
> // D function calling method
> void polymorphic_call(Foo f) {
> f.bar();
> }
>
> # Python subclass
> class PyFoo(Foo):
> def bar(self):
> print "PyFoo.bar"
>
> # Calling D function with instance of Python class
> >>> o = PyFoo()
> >>> polymorphic_call(o)
> PyFoo.bar
>
> Read that a few times until you get it. To see how Pyd handles this,
> read the above link. It's quite ugly.
If I understand things correctly, in the ideal setup you'd need a means
to expose an entire, or parts of, a class to Python. That is, for the class:
class Base {
void foo() { writefln("Base.foo"); }
void bar() { writefln("Base.bar"); }
}
instead of (or in addition to) the current state of affairs:
wrapped_class!(Base) b;
b.def!(Base.foo);
b.def!(Base.bar);
finalize_class(b);
it would be probably desirable to simply write:
defclass!(Base);
which automa(t|g)ically takes care of all of the above.
To do so properly, and to also solve the polymorphic problem that you
mention, defclass must define the following class:
class BaseWrap : Base {
mixin OverloadShim;
void foo() {
get_overload(&super.foo, "foo");
}
void bar() {
get_overload(&super.bar, "bar");
}
}
Then the BaseWrap (and not Base) class would be exposed to Python, along
with each of its methods.
If I misunderstood something in the above, please point out the error
and don't read the rest of this post. :o)
This kind of task should be easily doable with compile-time reflection,
possibly something along the following lines (for the wrapping part):
class PyDWrap(class T) : T
{
mixin OverloadShim;
// Escape into the compile-time realm
mixin
{
foreach (m ; methods!(T))
{
char[] args = formals!(m).length
? ", " ~ actuals!(m) : "";
writefln("%s %s(%s)
{ return get_overload(super.%s, `%s`%s); }",
ret_type!(m), name!(m), formals!(m),
name!(m), name!(m), args);
}
}
}
So instantiating, say, PyDWrap with Base will tantamount to this:
class PyDWrap!(Base) : Base {
mixin OverloadShim;
void foo() {
return get_overload(&super.foo, `foo`);
}
void bar() {
get_overload(&super.bar, `bar`);
}
}
Instantiating PyDWrap with a more sophisticated class (one that defines
methods with parameters) will work properly as the conditional
initialization of args suggests.
> The D wrapper class for Foo would look something like this:
>
> class FooWrapper : Foo {
> mixin OverloadShim;
> void bar() {
> get_overload(&super.bar, "bar");
> }
> }
>
> Never mind what this actually does. The problem at hand is somehow
> generating a class like this at compile-time, possibly given only the
> class Foo. While these new mixins now give me a mechanism for generating
> this class, I don't believe I can get all of the information about the
> class that I need at compile-time, at least not automatically. I might
> be able to rig something creative up with tuples, now that I think about
> it...
At the end of the day, without compile-time introspection, code will end
up repeating itself somewhere. For example, you nicely conserve the
inheritance relationship among D classes in their Python incarnations.
Why is that possible? Because D offers you the appropriate introspection
primitive. If you didn't have that, or at least C++'s SUPERSUBCLASS
trick (which works almost by sheer luck), you would have required the
user to wire the inheritance graph explicitly.
> However, I have some more pressing issues with Pyd at the moment
> (strings, embedding, and building, for three examples), which have
> nothing to do with these new features.
Update early, update often. :o) Please write out any ideas or issues you
are confronting. Looks like PyD is a good case study for D's nascent
introspection abilities.
Andrei
More information about the Digitalmars-d
mailing list