[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
[
List Home]
[handly-dev] A milestone build of Handly 0.5
|
Greetings handly-dev,
I'm pleased to announce the availability of an important milestone build that early adopters are most welcome to check out and comment on.
A few words about the core feature of this milestone: an entirely new design for the model API.
After much experimentation and reflection, I gave up on the idea of using mirrors as originally proposed in [1] in favor of what I consider a simpler and more efficient design. There are actually two problems with the mirrors: conceptual and computational weight. In a nutshell, mirrors cost time and memory to create; worse, as acknowledged in the seminal paper [2], when the distinction between base- and meta-level operations is either awkward or ambiguous, the mirrors can just get in the way. Indeed, it is not always obvious in practice whether a method argument or return value should be the element itself or its mirror. To quote from the paper, "In a non-uniform system, each option has drawbacks."
So the new design has exactly the same motivation as discussed in the original proposal, but tries to solve the problem without introducing additional concepts or incurring additional performance costs. Let me explain it in brief.
In the new design, IElement, ISourceElement, ISourceFile, ISourceConstruct, and IElementDelta are just marker interfaces. Implementors of these interfaces must also implement the corresponding "implementation interfaces" (namely, IElementImpl, ISourceElementImpl, ISourceFileImpl, ISourceConstructImpl, and IElementDeltaImpl), whose method names are all prefixed with 'h' in accordance with naming convention discussed in the original proposal. The provided basic implementation classes Element, SourceElement, SourceFile, SourceConstruct, and ElementDelta thus implement these implementation interfaces and also follow the "h-prefix" naming convention for all their non-static API methods (public or protected).
The classes Elements and ElementDeltas provide static methods for generic access to elements and element deltas of a Handly-based model, such as getName(IElement) and getElement(IElementDelta). Usually, those methods just cast their first argument to the corresponding *Impl interface, and call an appropriate method of that interface. As you can see, such simple "reflection facility" doesn't cost much in terms of performance and doesn't introduce entirely new concepts as opposed to the mirror-based approach. The associated "verbosity cost" of calling the static methods instead of the more usual "dot notation" has proven quite manageable so far, although something like Xtend extension methods would certainly be welcome for using this API. I'd like to note, however, that this facility is meant to be used mostly by "generic clients" such as common UI components; "ordinary clients" can use the model-specific API of a concrete model and thus are not exposed to the somewhat increased verbosity in notation.
Speaking of the model-specific API, a number of "extension interfaces" (namely, IElementExtension, ISourceElementExtension, ISourceFileExtension, and IElementDeltaExtension) are provided, which model implementors may choose to extend. The extension interfaces provide default methods that delegate to corresponding methods of the classes Elements and ElementDeltas. For example, the interface IElementExtension has a default method getName(), which calls Elements.getName(this). Thus, these extension interfaces can be effectively "mixed-in" to the model at the sole discretion of the model implementor.
So, the model implementor still has, basically, the same three choices as outlined in the original proposal:
1. The interface for a model element extends IElementExtension (and hence, IElement). This is similar to what has been available since Handly 0.1. An additional advantage vs. the original proposal is that this approach is now quite safe, since new members will not be added to the existing extension interfaces (not even default methods). Instead, new extension interfaces (like IElementExtension2, etc.) will be introduced when/if needed. So, the model implementor always retains control over picking and choosing desirable extensions and, thus, over the whole model API.
2. The interface for a model element extends the minimal IElement (only). In the new design, IElement is just a marker interface without any members that could pollute the model API, not even a single method like the hMirror() of the original proposal. Thus, the model implementor is free to form the model API to his liking, or retrofit an existing model interface to extend IElement while retaining backward compatibility.
3. The interface for a model element doesn't extend IElement, so it doesn't depend on Handly at all. The drawback is that explicit casts to IElement might occasionally be necessary, although it should not be a big problem in practice.
I hope this makes sense. Please feel free to ask questions/leave feedback.
Unfortunately, such radical re-design would not be possible without significant breaking changes; I'm really sorry about it. I have yet to describe all API breakages, but I'd like to highlight perhaps one of the more confusing: if you happened to use the methods getElementAt and getSourceElementInfo of the now removed class SourceElements, their corresponding replacements are getSourceElementAt2 and getSourceElementInfo2 in the class Elements, and NOT getSourceElementAt / getSourceElementInfo which are also present in that class. Also, please note that each of the "extension interfaces" needs to be extended separately. For example, ISourceFileExtension extends neither ISourceElementExtension nor IElementExtension. This provides flexibility for the model implementor to pick and choose desirable extensions, but may require some extra care. I'll try to come up with a detailed migration guide a bit later, but in the meantime just let me know if you have any questions or concerns with migration.
Thanks,
Vladimir