The Open Type System vs. Code Gen

Cedric Beust took a look at Gosu’s Open Type System a few weeks back here:

  http://beust.com/weblog/2011/05/10/open-type-systems/

Overall it was a very fair, if brief, look at Gosu’s Open Type System. There was one point at the end, however, that I want to examine:

While elegant, Gosu’s open type system is basically just saving you from a code generation phase.

This is not entirely true, but it is hard to see why at first blush.

What *is* true is that most problems that are addressed with code generation in Java today can be more elegantly addressed in Gosu by using the Open Type System. And, due to the fundamental laziness of the Gosu compilation model, it is possible to model problems with much larger potential type spaces (so long as only a reasonable subset of that type space needs to be materialized at compilation time) since it is not necessary to generate all types up front. So, yes, the Open Type System is a sort of elegant code generation mechanism.

However, it is more than that.

To understand this, consider the Protocols type loader that I wrote a while back:

  http://protocols.github.com/

Protocols are an entirely foreign concept to Gosu. They are a way to achieve “static” duck typing: you define a Protocol in a .proto file, which looks an awful lot like an interface. However, unlike Java or Gosu interfaces, a value’s type does not need to implement a Protocol for the value to be assigned to a variable that has a Protocol type. Rather, the value’s type must conform to the Protocol: for every function defined in the Protocol, there must be an invocable function on the value’s type that can be invoked safely.

An example will help clarify.

Given this procotol:

package test

protocol Protocol1 {
  function doIt()
}

Any value with a static type that has a method doIt() can be assigned to it:

class Class1 {
  function doIt() { print( "In Class1" ) }
}

class Class2 {
  function doIt() { print( "In Class2" ) }
}

var p : test.Protocol1 = new Class1()
p.doIt() 

p = new Class2()
p.doIt()

// p = new ArrayList() -- Won't compile, since ArrayList doesn't have a doIt() method... 
//                        Unless you add one with an enhancement... :)

So, even though Class1 and Class2 are entirely unrelated, we are able to assign them both to a variable with a protocol type to which they both conform. We can’t assign an ArrayList to that variable, however, since an ArrayList does not have a doIt() function (although you could make ArrayList conform to the protocol by using an enhancement to add a doIt() method if you wanted to. But why would you do something crazy like that?)

I was able to implement Protocols in Gosu precisely because the Open Type System gave me more tools than simple code generation: I was actually able to implement the unique assignability semantics of my Protocol types using the IType#isAssignableFrom() method.

This is a level of control and power that mere code generation does not have.

Winning.


3 Comments on “The Open Type System vs. Code Gen”

  1. Stephen Haberman says:

    Nice! Structural typing is awesome.

    I was poking around the source of the protocols project–where does the actual method invocation happen, e.g. calling Class2.doIt vs. Class1.doIt?

    Just playing (a naive) devil’s advocate, it seems like doing structural typing in the open type is somewhat a mixing of concerns–if structural typing was more generally implemented across the language, then all types (open or regular) would get it, which seems like a more consistent user experience? …or is this already the case?

    • Carson Gross says:

      The actual invocation of the method occurs in the MethodCallHandler, see the bottom of:

        https://github.com/protocols/protocols/blob/master/src/protocols/ProtocolMethodInfo.java

      I just use Gosu’s reflection system to invoke the correct method. Note that this is *not* fast. 🙂

      I like structural typing and was initially arguing for including it in the language, but we have a rule where there has to be unanimity regarding a feature before it goes in, and there was not in this case. So I decided to use it as a way to show off the Open Type System instead.

      • It’s probably worth mentioning there are different ways of expressing structural typing. Primarily there are two categories: declaration site and use-site.
        Carson’s protocol type loader provides a nice declaration site solution. Its primary advantage is that it saves the *user* of type from having to express the protocol relationship at the use-site, it just works. Always a big win. The downside is the interface of the protocol has to be known in advance of its usage. This is not always the case. For instance, sometimes the reason you want structural typing is to conform to an API over which you have no control. You want Class X to be assignable to an existing interface Y. X and Y aren’t yours to modify or are part of an API which is for all intents and purposes locked. Protocols won’t help you here.
        Enter use-site structural typing. Essentially what you want is to perform an explicit cast from X to Y. Of course the downside with any use-site solution is the fact that it’s use-site; the burden is on the user of the type and not the type creator. Alas, there is no clean alternative in this particular situation. You have to tell the compiler that it should make sure X can satisfy Y. There are more ways to skin this cat than I care to dive into, but three stand out:
        1) Provide syntax for a structural cast e.g., (Y*)x or (x as Y*)
        2) We could leverage Gosu enhancemnets and declare that X is a Y (wait, that’s declaration site typing…. yep). The problem with this, among others, is that it’s all or nothing; you might unintentionally use X as a Y.
        3) Since we operate in the JVM, “cast” is pretty much a code word for proxy, so why not make it easy to make proxies for this case? With the magic of enhancements it could look like this:
        x.duck( Y )
        I like the term “static duck typing” since that is essentially what is going on with this flavor of structural typing, hence the “duck” reference. Anyway, that is what we pretty much settled on as far as providing structural typing. We didn’t want an extra core language construct since protocols could be implemented as a type loader; another big win for Gosu’s open type system! So there you have it.
        Scott


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s