I hate to beat a dead horse and all . . .

. . . but this is pretty egregious.  I got so sick of writing my own partition code in Java (since I’m so used to being able to do it easily in GScript) that I pushed it out into a utility method so I wouldn’t have to rewrite the same code over and over.  Thanks to the overhead of Java generics, anonymous inner classes, and type declarations, I’m not sure it was even a win.  Now my code looks like:

  Map<String, List<FormPatternLookup>> partitionedLookups =
    new CollectionUtil.Partitioner<FormPatternLookup, String>() {
      public String partitionKey(FormPatternLookup formPatternLookup) {
        return formPatternLookup.getState() +
          ";;" + formPatternLookup.getUWCompanyCode();

Ugh.  If I had written this code in GScript, it would have been:

  var partitionedLookups = lookups.partition(\l -> l.State +
    ";;" + l.UWCompanyCode)

I mean, honestly . . . that’s pretty brutal.

Pragmatic Type Systems

What do static type systems provide and what do they cost? It’s tough to know what to make of the dynamic vs. static typing arguments going on right now on sites like Artima without having solid answers to these two questions.

The Benefits

Static type systems have been developed mainly by the academic community, and they focus on soundness and provability. This is understandable: academic computer science is largely a subset of mathematics, and so adopting the rigor of the mathematical community is natural. This rigor leads to the conclusion that the primary goal and benefit of a static type system is that it provides (or *should* provide) proof that your program functions as you expect.

A less extreme version of this view holds that the main benefit of a static type system is that, even if it doesn’t prove your program is correct, it will help you catch errors at compile time rather than run time. The common rejoinder to this argument is that we should all be writing tests anyway, and the tests will catch the errors for us rather than the compiler. That’s true to a point, but it is nice to have the compiler point out exactly where the trouble spots are after you’ve merged in someone else’s changes to your local machine. Running a suite of tests and sifting through the output can be tedious and slow, and compiler errors tend to be clearer and more local to the actual error than test failures. Sure, the errors compilers catch are often trivial, but they find them quickly and easily.

A third, and very different answer is that static type systems make it easier to provide excellent tool support. Things like code-completion, code analysis and refactor tools. Dynamic languages can provide some low-rent versions of these features, but they are either complicated (smalltalk) or poor implementations (ruby IDEs.)

This third benefit is easily the most compelling. IDE’s and other tools help average, fallible humans get code out the door and clean up after themselves on a day-to-day basis. It’s more important that the editor tell me what I can do with a variable than it verify what I’ve done is correct after the fact. The tools provide discoverability and allow developers to leverage up their memory banks, with the tools themselves remembering all the details about types. Once you really start ripping through code with a good IDE like IntelliJ, it’s hard to think about coding without it, and it’s hard to think about how to write an IDE as good as IntelliJ without static typing.

The Costs

The most obvious cost of a static type system is syntactic: you simply have to write more code since you have to declare the types of things. Java is a worst-case example of this: you end up writing types out over and over again on the left and right hand side of assignments.

A second cost of static type systems is conceptual complexity. A good example of this is java’s generics implementation. Josh Bloch made headlines a while ago[1] when he came out against his longtime friend Neal Gafter’s closures proposal. In his talk he cited wildcards as the primary source of complexity in java’s generics. (Note that wildcards were added to preserve type-safety: they aren’t generally of much use to tools.)

Type inference, which helps with the syntactic cost of static typing, can also add to the conceptual complexity if the inference is too aggressive: it can cause incomprehensible, difficult to resolve compilation errors. You can see this in java occasionally with generic method inference. If you’ve ever used an ML-derived language for a non-trivial project, you’ve probably seen this as well.

The GScript Approach: A Pragmatic Type-System

GScript aims to maximize the benefit of static typing while minimizing the costs. Since there is no mathematical formula tying these two opposing goals together, we simply had to go with our guts. (Not that there is no guidance available. Scott McKinney, the creator of GScript, often cites a paper[2] from Microsoft as a source of inspiration when making these tradeoffs. He also follows Anders Hejlsberg’s work on C# 3.0, a very pragmatic language.)

Below are two examples of pragmatic decisions we’ve made with respect to GScript’s type system. Both involve tradeoffs that we feel maximize the benefits of the static type system while minimizing the burden of it.

Pragmatic Type Inference

GScript provides local type-inference:

  var myList = new ArrayList()

You do not need to declare that myList is a List, the GScript compiler will determine that for you. This is especially nice with generics:

  var myMapOfLists = new HashMap<String, List<String>>()

However, unlike some ML-derived languages, GScript does not infer the types of function arguments or return types:

  function joinList( lst : List ) : String {
    var str = new StringBuilder()
    for( elt in lst index i ) {
      if( i > 0 ) str.append( ", " )
      str.append( elt )
    return str.toString()

Note that both the parameter type, List, and return type, String, are annotated.

This is conceptually simpler for both the compiler writer and the GScript coder, since all type information is local to the function. It also allows for a decoupling of types that I will discuss in a later article.

So GScript steers the middle course between no type-inference (java) and total type-inference (ML-like languages.)

Pragmatic Generics

As you can tell from the code above, GScript, like java 1.5 and greater, has a generics implementation, where you can declare a list composed of a certain type:

  var listOfStrings = new ArrayList<String>()

GScript does not have wildcards, however. Instead, we adopted covariance of generic types, so a List<String> is assignable to a List<Object>. This hugely simplifies generics for the end user: generic classes behave just like arrays, which everyone is used to. We do sacrifice some type safety and a bit of expressiveness (see my note on the Microsoft paper below) but we don’t sacrifice anything with respect to tools, and we lop off a huge chunk of complexity.

Static Typing with a Light Touch

With the two features outlined above, GScript manages to have a reasonably type safe static type system that supports tools quite well, while having a relatively small syntactic and conceptual overhead. With this light touch it competes well syntactically and conceptually with dynamic languages such as ruby or python for average, day-to-day development. The type system, for the most part, gets out of the way of the developers except when they need it.

So the costs of a pragmatic static type system are pretty low and the benefits are pretty high, as long as you have access to a language like GScript.

We’re working on that.

[1] – The Closures Controversy – Chapters 12-16
[2] – Static Typing Where Possible, Dynamic Typing When Needed:
The End of the Cold War Between Programming Languages

One interesting note on this paper is that that the authors want two incompatible features: they mention an IComparer interface as their example of generics, but then advocate covariance of generic types. Unfortunately, IComparer is a contravariant type! That is, you can safely assign an IComparer<Object> to a variable of type IComparor<String>, but you can’t safely assign an IComparor<String> to a variable of type IComparor<Object>.

Tradeoffs, tradeoffs, tradeoffs.

Engineering As Failure Avoidance

The common view of engineering is that you’re building something up:  successively adding parts, features, layers, etc. to eventually achieve some sort of functional goal.  That general view holds whether you’re building a bridge or a piece of software.

There’s another way to look at engineering, though, and that’s as the art of failure avoidance.  When building a bridge, you have to anticipate all the ways that the bridge could fail and plan around them:  worst-case weight loads, earthquakes and high winds, extreme heat or cold, etc.  A good bridge, in the structural sense, is one that manages to avoid all the possible failure modes.  You might think of this as the Anna Karenina rule for engineering:  all successful projects are alike (they don’t fail), while all unsuccessful projects fail in their own unique ways.

Good software engineering is the same in that respect, with the difference that software can generally fail in far more ways than a physical structure can thanks to the magic of things like concurrency, state, and combinatorial explosions of possible inputs.  So what does it mean to engineer to avoid failure rather than just to build features?  Here are a few rules I try to keep in mind.

Test Like You’re Trying To Break It

Engineers just tend not to do the best job at this; you wrote the feature to do X, you test that it does X, and you move on.  But the bugs that approach catches are just the easy ones, and the nastier bugs are the ones that are a result of simply not thinking about certain scenarios or combinations of cases.  Sure, it does X if you use the feature as intended, but what if you try to abuse it?  What if you deliberately put in invalid input, or try to use it at inappropriate times, or otherwise violate any implicit assumptions about when and how the feature will be used?  One of the benefits of test-driven development is that it forces you to at least consider those use cases more.  In the end, though, one set of eyes generally isn’t good enough, which is why pair programming/testing can help and why you still need some amount of dedicated QA time even if your engineers write all the tests you can think of.  But as an engineer, the more you can really try to break the code you’ve just written, the better your tests will be.

No Wishful Thinking

One of the biggest sources of software failure, in my opinion, is simply the fact that software engineers always want to believe that their software works and, given a lack of immediate hard evidence to the contrary, will tend to want to believe that things will work.  Optimism in general is good for your morale, but when it crosses over into wishful thinking you get into trouble (i.e. “I’m confident in our ability to deliver on the features we’ve agreed upon” becomes “We haven’t really tested X yet, but I’m pretty sure it’ll work” or “We haven’t really tested that kind of load but I’m pretty sure we can handle it” or “The schedule looks tight but I think we can make it”).  It helps to have at least one surly, grumpy pessimist on your team to provide a check against most people’s natural tendancy to want to assume things will work out okay.  The rule is generally that if you haven’t demonstrated that it’ll work it probably won’t.

Think About The Worst Case

Another classic engineering failure mode is to only consider the expected or average case and not to plan for or test out the worst case.  For example, if you’re displaying a list of items, how many items will that list have on average versus the 95th percentile or absolute worst cases?  If the list might have 30 items on average but could have 10000 in a worst case, you’re going to need to design your software such that it performs at least acceptably under the worst case, even if it doesn’t appear that often.  It’s easy to conflate “how often X happens” with “how much work I should put in to handle X” but in reality you need to make sure you handle those 1% cases gracefully (which doesn’t necessarily mean “optimally”) even if that doubles the effort.

Optimize For Debugging And Bug-Fixing

No matter what you do your software is going to be buggy (well, maybe not if you’re Don Knuth, but for mere mortals); hopefully your testing procedures allow you to find those problems before they go into production, but an important part of failure avoidance is fixing them when they come up.  There are really two sides to that coin:  tracking the problem down and fixing the problem.  Tracking the problem down generally requires the right set of tools and the right sort of code organization; well-structured code is easier to debug, and explicit code, even if it’s verbose, tends to be much easier to debug than implicit or declarative code where “magic” happens in some incredibly general way that’s hard to put a breakpoint on.

Being able to fix bugs requires something of the same approach.  As a company that ships highly configurable products, though, we have an extra set of issues that come up when you ship frameworks and tools.  If too much is built into the framework in a way that isn’t controllable by the person writing code on top of the framework, you can end up without a bail-out when bugs do occur.  So as much as declarative, implicit programming can be useful, it’s usually good to have an imperative, explicit bail-out when necessary to allow working around shortcomings in the underlying platform.

Consider The Failure Modes

Related to the previous point’s observation that failure is, in some sense, inevitable, it’s important to consider what the failure modes actually are and to design the system in such a way that they’re as benign as possible.  For example, if an automated process can make the right decision 90% of the time, ideally you’d like to identify the 10% of the cases where the system can’t figure out the right thing with 100% certainty and kick that out to a user to make the final call.  If you’re writing a security framework, you need to consider if you want the inevitable mis-configuration to fail open (such that anyone can access things) or fail closed (such that no one can).  In the aforementioned case of a huge list, perhaps you can get away with simply capping the number of entries that can be displayed, or perhaps one page being glacially slow 1% of the time is fine provided you can keep it from slowing the entire server or database to a crawl.  In other words, you don’t have to write the perfect system, but you should write one that avoids doing anything you’re not sure is correct, at least in cases where correctness matters.

If You Can’t Get It Right, Don’t Do It

Lastly, some features just weren’t meant to be built.  They’re too complex, or too ambigious, or otherwise too hard to get right.  Discrection is, as they say, the better part of valor, and it’s important to know the limits of your tools, schedule, and team abilities.  It’s almost always preferable to have 50 100% correct features than 100 50% correct features.

The Necessity of Type Inference

Type inference is a subject near and dear to our heart here at Guidewire; one of the primary features of GScript from the very start has been type inference of local variables, and it’s proven over the years to be one of the more invaluable features in the language.  So invaluable, in fact, that it’s hard for me to stomach the prospect of using statically-typed languages without type inference.

For GScript, we’ve actually taken a relatively limited approach to type inference, at least compared to languages like Haskell or OCaml; we only infer the types of local variables or private member variables that have an initializer statement.  Method parameters and return types and non-private mamber variables always have to be explicitly typed, as do variables that have no initializer.  It might seem like that doesn’t buy you a whole lot, but in reality it makes your code tighter and more flexible without sacrificing any safety.

The code elimination part is fairly obvious if you try it out.  In Java, creating a new generified Map might look like:

Map<String, String> myMap = new HashMap<String, String>();

In GScript, it’s:

var myMap = new HashMap<String, String>()

The astute reader will notice that those don’t do exactly the same thing, but I’ll get to why that’s not really a problem later on.  In Java you can lessen the pain somewhat by using an IDE that automatically extracts variables or by ignoring the compiler warnings and dropping the type parameters on the right-hand side, but relying constantly on the IDE to do every little thing is a bit painful.  If you need to go back and change things, rather than just changing the type you have to use an IDE refactoring to make sure it changes the variable declarations.  Situations where you assign to the result of a method call are even more of a waste in Java:

Map<String, String> myMap = myMethodCall();

instead of:

var myMap = myMethodCall()

One of the classic developer refrains is Don’t Repeat Yourself, and explicit type declarations for things that are obviously inferrable clearly violates that.  If you want to change the type returned by myMethodCall(), you again have to be sure to use an IDE refactoring to make sure all the variables are changed, and you could end up needing to touch plenty of files to propagate it through.  The redundant type declarations add friction to your code base that not only make initial code creation harder but also make subsequent changes more difficult.

There’s a more subtle way in which type inference enables your code base to be flexible, though:  it provides some measure of duck-typing in the right situations.  For example, if you have the code:

var myList = myMethodCall()

It doesn’t matter whether myMethodCall() returns a List<String> or a String[] or any other variant as long as the subsequent calls are still valid.  Drop-in replacements for classes or interface extraction become much easier as a result of type inference, since the changes don’t have to propagate as heavily through the code.

You could go further with type inference than we have and attempt to infer the types themselves, infer union types, or infer method return types, but we’ve chosen to stop at variables for a few reasons.  The first one is that method parameters, return types, and variables exposed outside of the current class all create a contract between two parts of your code, and in those cases you generally want to control that contract more explicitly and use interfaces like List and Map instead of concrete implementation classes like ArrayList and HashMap.  Local variables and private member variables, by definition, don’t leak outside of a fairly limited scope, so it’s generally not worth worrying about whether they’re interfaces or implementation classes.  Secondly, it simplifies the rules about when explicit type declarations are needed; we could try to infer method return types but there would be some arbitrary conditions where that broke down thanks to cyclic dependencies between classes and methods and variables, and it wouldn’t be at all obvious to the user when or why.  Having a simple set of rules, we hope, makes that less confusing.  Lastly, anything more aggressive, like inferring parameter types based on usages, would require a completely different type system from what we have in GScript and a completely different direction for the language.  That’s not to say it’s a bad thing (I think a lot of what OCaml does with types is quite cool, for example), it’s just a radical departure from the more traditional Java/C++/C lineage than we want to make.

So given all the advantages, why does Java still avoid adding some amount of type inference in?  I honestly don’t know.  If anything should have provided the impetus to add type inference, it was the introduction of generics, which have caused countless redundant keystrokes over the years.  Personally I can’t construct a credible case against it, and I haven’t really heard one presented to me, though I do hear two arguments fairly often.

The first argument is the “use interfaces over impl classes” argument.  Yes, every good Java programmer knows that you want to use more generic interface types like List and Map instead of concrete implementation types like ArrayList or HashMap to protect the flexibility of your implementation and to increase encapsulation.  And that’s true, up to a point:  the point at which your implementation is exposed to the outside world.  For local variables and private member variables, however, it’s all implementation details at that point.  If you’re assigning the variable to the result of a method call, that call should be coded to return a List instead of an ArrayList.  If you’re passing the variable out to a method call, the parameter should be typed as a List instead of an ArrayList, so there’s no encapsulation leak there.  So the only thing you protect against by using interface types for local variables is against using implementation-specific methods without realizing it on objects created locally within that method or within the class.  That’s just not much of a danger in my book; you’re protecting a method or class against changes to itself that couldn’t possibly affect anything outside of itself, which seems a wee bit silly:  if I find that I’m using methods on ArrayList that aren’t on List, and I change the local variable I’m using so it’s a LinkedList, I’ll just deal with the fallout right then in that method.  It’s certainly not worth avoiding type inference to try to “protect” against that situation.

The second argument is that type inference makes the code harder to read.  This one has a little more credence, but most of the time it’s pretty obvious what’s going on.  New instance creation is always obvious, and in most other cases the variable is well-enough named, the method being called is well-enough named, or subsequent usages are obvious enough to make it easy to figure out.  The worst case is that you have to dig one level in to see what type a method is declared to return.  Any decent modern IDE will basically do this for you, though:  code-completing on the variable will tell you what type it is, bringing up the javadoc for the method being called will tell you, clicking through to the method will tell you, etc.  In our IDE, we’ve even added a shortcut, Control-T, that shows you the type of any variable or expression at any point in the code.  Even then, it’s not that much different than looking at a variable somewhere that’s been declared elsewhere:  you don’t repeat the type information on every usage and somehow people find a way to muddle through.  Yet somehow the redundant information on the declaration is critical and can’t be lived without?  Again, it seems like a post hoc justification for being afraid of something different, and in practice it’s not that much of an issue:  without any IDE it’s not that much different from what you have to do now to deal with variables and method calls you aren’t familiar with, and with an IDE it’s a complete non-issue.

To me the issue is pretty clear-cut: a type system is a tool to help catch errors early and to improve the ability for tools to understand and manipulate code, but it does so at a heavy cost in terms of verbosity and inflexibility.  Dynamically typed languages obviously have a huge advantage on those two fronts.  Type inference is a way to preserve the benefits of static typing while reducing its overhead, and at this point in the history of language, compiler, and IDE development it should be a part of every modern language, including Java.

Frameworks and Foreign Languages

I spent part of my recent vacation in Spain, and since I don’t really speak Spanish I was forced to get around by simply parroting certain key phrases. I always feel strange speaking phrases in a language I don’t really understand, since I don’t really fully understand what it is that I’m actually saying; I might be able to swap out a few choice nouns here or there (i.e. ask “where is the museum?” instead of “where is the train station,”) but my ability to do transformations that would be trivial in a language I really understood (i.e. “where was the train station?” or “how far is the train station?”) simply isn’t there. But despite all that, plenty of people are able to navigate around and vaguely communicate in foreign languages through simple rote memorization of common phrases, despite not really understanding what it is that they’re saying or even what the components are that make up those phrases.

The phrase book approach is entirely different from what you’d do if you were to take a class in a foreign language; in that case you’d start from the ground up with some simple nouns and a few common verbs, learning the irregular verbs as well as the common conjugations of regular verbs, and then gradually expand out to other tenses and an expanding vocabulary.  That sort of learning is much more thorough and is generally what you need to really become fluent in a language that you intend to speak, but if you just need to get around for a week or two knowing a few key phrases is generally enough to muddle through.

Upon returning, I was taking some time to try to explain the PolicyCenter delegation model, a pretty complicated concept, to another developer when I came to the realization that perhaps I was going about things the wrong way.  Just like there are two ways to go about learning or using a language, there are two different strategies for learning a framework:  you can try to learn all the nuances or you can just learn the basic phrases, and only really dig in once it’s clear the commitment will be worth it.

The reality is that most people will learn a framework more via the phrasebook approach, where they’ll simply cut-and-paste-and-tweak their way to accomplishing their goal.  Eventually, if they use the framework enough, they’ll put in the work to become fluent in it and understand why things are put together the way they are, or why you generally only use certain combinations of options.  But just as it takes a long time to develop any usable vocabulary if you attempt to start learning a language from the ground up, with an investment that’s simply far too high for something you’ll only need for a week of your life, learning a framework inside out is way too time-consuming and presents an initial barrier to entry that’s far too high.

Unfortunately, many of us designing those frameworks don’t think that way:  we put a lot of time into making sure the API is right and the design solid, and into testing things and making sure it all fits together and is flexible in the right ways while not being overly complicated, and we often unconsciously expect that the people using our frameworks will take the time to understand all those nuances and really learn the language rather than just copying out of a phrasebook.  But that’s just not how it works, and even people that will eventually want to learn the full language will start out trying it out by copying and pasting examples.  Without those examples, people just flail, the barrier to entry is too high, and much of the hard work of creating a great framework is wasted because it ends up being under-utilized.

Whenever I come across a general guiding principle for my development work, I try to come up with some simple catchphrase to encapsulate the idea, and for this one it’s “optimize for copy-and-paste.”  If you want to create a great, configurable product, you can’t just drop people a toolset and some documentation; you have to give them a large, realistic set of working examples that do close enough to what they need to do for them to visualize how to get there.  And you can’t just treat your example code as “throwaway” code that doesn’t need to be good, since people will be using it as their starting point, so they’d better be good examples that you’d be okay with having someone else use in production.

Why I’m Not a Fan of Java’s Auto-Unboxing

Starting with Java 1.5, the Java compiler started automatically taking care of converting boxed versions of primitive types into primitive types where necessary, and vice-versa.  While I’m generally in favor of anything that makes programming languages easier to use, provided it doesn’t overly-complicate things, I’ve never been a huge fan of the way it was done.

Most languages either have C or C++ style primitives or object-style numbers and booleans, but Java has always had an odd mix of the two.  The easiest thing for a programmer is to simply go all the way to the object model and be done with it; my assumption is that Java didn’t do that both for the sake of continuity with C and for the sake of raw performance.

But unfortunately, the solution of auto-converting between the two worlds doesn’t really solve the problem that there are differences there that you have to be aware of.  The main difference is around null; a primitive type can’t be null, and a boxed type can, so every time you unbox an object you have to worry about how to handle null values.  If you do the unboxing by hand, at least you might have to think about it, and if you don’t at least it’ll be obvious what the error is.  But the auto-unboxing both manages to not handle null for you and manages to completely hide the errors when they do happen, which is basically a huge lose-lose in my book.  I managed to spend far too long the other day trying to figure out why the following line of code was throwing an NPE:


My instinct is that an NPE is caused by a method call or field reference on a null object, so clearly either “modifier” or “pattern” has to be null here, right?  So after staring hard at the code for several minutes to try to figure out how that could be possible I had to go to the trouble of setting everything up in the debugger, walking through the code . . . and finding out neither was null.  Huh?  Of course, not at all obvious from reading the code is the fact that getDisplayEligibility() returns a Boolean rather than a boolean, and in this case it was returning null, meaning that the ! operator was throwing the NPE.

Normally, if you tried to apply ! to an object you’d get a compile error, but thanks to the magic of auto-unboxing Java now has all sorts of exciting ways to throw NPEs that you wouldn’t think to find.  Right after fixing that bug I ran into another, very similar error:

public boolean isScheduleRate() {
  return getPattern().getScheduleRate();

Again, getScheduleRate() actually returns a Boolean instead of a boolean, so if it’s null the return statement will NPE.  Combined with the way Java implemented the new for loop, that means that instead of an NPE only being possible on the . operator or [] references, you now have to look out for !, return, arithmetic operators like + and -, and for loops.

In GScript we, for better or worse, auto-coerce null to false or 0 depending on the context, which also requires you to understand that that can happen, but at least prevents the sort of hidden NPEs that the current Java approach causes.  It probably behaves like you’d expect most of the time and is generally consistent with how other languages behave.