Time for std.reflection

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Sat Jul 21 14:44:51 PDT 2012


Walter and I discussed the idea below a long time (years) ago. Most 
likely it's also been discussed in this newsgroup a couple of times. 
Given the state of the compiler back then, back then it seemed like a 
super cool idea that's entirely realizable, it would just take time for 
the compiler to become as capable as needed. Nowadays we're in shape to 
tackle it.

Here "it" is.

Back when runtime reflection was being discussed, my response was "let's 
focus on compile-time reflection, and then we can do run-time reflection 
on demand as a library". Though this might sound sensible, I initially 
didn't have a design. Now here's what we can do.

Currently we have information about symbols as __traits(...) intrinsics 
wrapped in nice but scattered ways. Now that CTFE is good enough to 
manipulate structs and arrays thereof, we have the possibility to 
finally approach things in a nicely unified, structured way.

First, we need to prime std.reflection with a few abstractions that 
characterize entities in a D program.

class ModuleInfo {
@property:
     string name();
     ImportInfo[] imports();
     DataInfo[] data();
     FunctionInfo[] functions();
     ClassInfo[] classes();
     StructInfo[] structs(); // includes unions
     TemplateInfo[] templates();
     EnumInfo[] enums();
     bool hasStaticCtor(), hasStaticDtor(),
       hasSharedCtor(), hasSharedDtor();
}

Probably there are a few more pieces of data, but you get the idea. Then 
for each of the entities mentioned above we have a similar definition. 
For example:

enum Protection { isPublic, isPackage, isProtected, isPrivate }

class ClassInfo {
@property:
     string name();
     string baseName();
     string parentName(); // if applicable, null otherwise
     string[] interfaces();
     bool isShared();
     Protection protection();
     DataMemberInfo[] data();
     MethodInfo[] methods();
     Object defaultConstructor();
     ...
}

Then for an e.g. method declaration we'd have:

class MethodInfo {
@property:
     string name();
     bool isStatic(), isFinal(), isOverride();
     Protection protection();
     string[] parameterTypes();
     string[] parameterNames();
}

Some details may vary, e.g. some may be straight members instead of 
properties etc. (I used properties to allude to use of lazy gathering of 
information).

So so far we have a nice collection of structured data associated with 
the entities in a D program. Note how this structuring differs yet has 
similar power to the primitives in std.traits; std.traits offers 
unstructured bits of information on demand (e.g. ParameterTypeNames) 
etc. but the objects above group information together per entity 
declared. All of the above goes in std.reflection, of course.

===========

On to primitives that return such data.

Given that D can (since relatively recently) create and manipulate class 
objects during compilation too, it follows that the classes above can be 
accessed in two ways - through compile-time API and run-time API. When 
possible, the APIs may even use the same functions; some other times 
they will be necessarily different.

There are two possible approaches to discovering such information. One 
is by fetching the ModuleInfo for the whole module and navigating it. 
Another one is by using search primitives from strings.

So we should have e.g.

// inside std.reflection
ModuleInfo getModuleInfo(string moduleName);

so a CT call would go like:

// client code
static info = getModuleInfo("std.algorithm");

whereas a run-time call would be:

// client code
auto info = getModuleInfo("std.algorithm");

In the latter case, the module needs to save all needed information for 
ri, so it should plant this:

// inside std.algorithm
mixin(makeModuleInfoAvailableDynamically());

The mixin would generate all information needed and would store it for 
later dynamic use.

A search API would go like e.g.

ClassInfo getClassInfo(string className);

In this case the class name could be qualified with module information etc.

===========

With this design we unify compile-time and run-time type manipulation in 
simple ways, by defining structured information about declarations that 
can be queried during compilation or dynamically.

Please chime in with thoughts. Would someone want to pioneer this project?


Andrei


More information about the Digitalmars-d mailing list