Articles

A Problem of Decoupling?

Recently, I’ve been working on improving the core framework that underpins the Whiley compiler.  This provides a platform for reading/writing files of specified content in a structured fashion.  Like Java, Whiley provides a hierarchical namespace in which names live and can be imported by others.  Let’s consider a simple example:

package zlib.core

import Console from whiley.lang.System
import zlib.util.BitBuffer

Here, we have two modules that must exist in the global namespace: whiley.lang.System and zlib.util.BitBuffer. As with Java, they co-exist in the same namespace, but do not necessarily originate from the same physical location (i.e. whiley.lang.System is located in the wyrt.jar, whilst zlib.util.BitBuffer is in a file system directory somewhere).

The Whiley compiler takes care of this through the Path.ID and Path.Root abstractions.  A Path.ID represents a hierarchical name in the global namespace (e.g. whiley.lang.System); a Path.Root represents a physical location which forms the root of a name hierarchy (e.g. a jar file or a directory). Thus, the global namespace is made up from multiple roots and, to find a given item, we traverse them looking for it (we’ll ignore the possibility of collisions for simplicity). To illustrate, here’s the (slightly simplified) Path.ID interface:

public interface ID {

  /**
   * Get number of components in this ID.
   * ...
   */
  public int size();

  /**
   * Return the component at a given index.
   * ...
   */
  public String get(int index);

  /**
   * Get last component of this path ID.
   * ...
   */
  public String last();

  /**
   * Get parent of this path ID.
   * ...
   */
  public ID parent();

  /**
   * Append component onto end of this id.
   * ...
   */
  public ID append(String component);
}

This all seems simple enough, right? Well, yeah it is!

So, what’s up? Well, the thing is, the Whiley compiler is not the first system to address this problem! Eclipse, for example, adopts a similar approach through the IPath interface. A cut-down version of this interface is:

public interface IPath {
  /**
   * Returns the specified segment of this path, ...
   * ...
   */
  public String segment(int index);

  /**
   * Returns the last segment of this path, ...
   * ...
   */
  public String lastSegment();

  /**
   * Returns the number of segments in this path.
   * ...
   */
  public int segmentCount();

  /**
   * Returns whether this path is a prefix of the given path.
   * ...
   */
  public int isPrefixOf(IPath path);

  /**
   * Return absolute path with segments and device id.
   * ...
   */
  public IPath makeAbsolute(IPath path);

  ...
}

Hopefully, you’ll notice both similarity and difference between the Path.ID and IPath interfaces. In fact, IPath has quite a few more methods which are not present in Path.ID. However, it should be clear that the functionality described by Path.ID is a subset of that described by IPath (albeit with slightly different names).

Obviously, I want to integrate the Whiley compiler with Eclipse (i.e. make an Eclipse plugin for Whiley). At the same time, I want to reuse as much of Eclipse’s functionality as possible within my plugin — otherwise, I’m just adding more bloat to an already bloated system, and potentially compromising the effectiveness of my plugin. I’m prepared to go to some lengths to enable this, even to the point of changing my Path.ID interface to bring it more inline with IPath; however, I’m not prepared to make the Whiley Compiler depend upon Eclipse — that is, no import org.eclipse..* statements in the Whiley compiler.

How can I do this? Well, the obvious solution is to provide an Adaptor in the plugin which implements Path.ID and wraps an IPath. This probably requires adding a Factory interface (e.g. Path.Factory) for creating Path.ID instances, since the Whiley compiler needs the ability to construct Path.ID instances and test for their validity.

The adaptor works, right? Yeah, it does. But, it amounts to layering one abstraction on top of another when they’re really the same thing. It would be nice if there was a mechanism for binding abstractions together. For example, in the Eclipse Plugin, it would let me say: let an IPath be a valid Path.ID with the following binding between names. But, perhaps that’s too much wishful thinking…

6 comments to A Problem of Decoupling?

  • Andrew

    Why not just make Path.ID’s interface compatible with IPath, so you can swap out the naked Path.ID interface with the IPath as a base interface (ahh inheritance of interfaces :) )

    My guess is that IPath will probably provide more methods then Path.ID, so you might want Path.ID to actually be an abstract class that can fill/stub the functionality.

    Technically you would only use Path.ID extends IPath when you want eclipse support (via an optional jar or similar) so you wouldn’t _require_ eclipse.

  • Hi Andrew,

    Yeah, so basically have different versions of Path.ID, depending on whether I’m compiling for Eclipse or stand-alone. Yeah, that’s workable … but still not exactly elegant :)

    D

  • Andrew

    I was thinking about swapping the class out at run-time, I’m not sure how tolerate Java is about that.
    But compile time would work too.

    Obviously when building new things that use Path.ID you’ll have to test against both versions.

  • Kevin

    you want clojure protocols/multimethods or haskell type classes

    in clojure Path.ID would be a protocol that the plugin could extend to IPath

    java style interfaces have the problem of not being injectable/extendable to types you don’t own

    http://clojure.org/protocols
    http://book.realworldhaskell.org/read/using-typeclasses.html

  • egon

    What I would do in this case is, add some class overriding methods to Path.ID. (without making a separate factory, as it’s unnecessary)


    private static Class actualClass = ID;

    public static ID create();
    public static void setClass( Class clz );

    And then set adaptor with setClass, and in create use actualClass to create the instance.

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>