Wednesday, August 25, 2010

Who are you writing code for?

Currently, I have come to realize that I have a flawed methodology for how I write code. My main solace is that it appears to be a flaw shared by many of my peers. This flaw, simply put, is that I try and design all of my code with the belief that somebody else will try and use it in something they do.

The intent here is usually altruistic. I want to share somebody else the burden of having to implement a few ideas that I had in my head. Just as I will do a quick search for how others have accomplished something, so too do I hope that somebody might find what I have done in one of their quick searches.

I will confess this is not always the case. Sometimes, I just want to force others to do something the exact way that I am doing it. In the cases where somebody is trying to add to something I have done, I have little qualm with this thought. (Almost amusingly to my point, the person most likely restricted in how they can add functionality to my code will be myself.)

Of course, despite which of the reasons I am holding as to why I am trying to make something reusable, the reality is that I will be forcing whoever tries to implement my idea to do so in the exact same way that I did. Some of this will be through my own lack of understanding when it comes to API creation. Most will come to down to my simply not seeing how somebody else would want to reuse something.

This inability to see possible uses should be no surprise. The people that actually interact with my code more than anyone else will be the users of the system I am helping to write. While they will never see a line of code I have written, they will be quite familiar with what it accomplished. Worse, for me, is that they will likely have envisioned something subtly different than I did when they want to use the application.

It is a common enough event to have to take some customer feedback into consideration when building something for them. How could I possibly think I could design some code for other developers, with limited feedback from them?

Who then should I write for? It makes little sense to write the code simply for the end user. After all, they simply see what it does, not how it does it. They are far from concerned over whether or not I used enough patterns when I implemented their system. They probably don't even know the different languages and styles I have to choose from. Why should they?

I am tempted to say that code should be written strictly for myself. I am reading it more than anybody else. I am the one writing tests for it. I am likely the one that will have to track down bugs in it. So, if I wish to try and write everything using a declarative style because that is easier for me to understand the intent when I look at it later, then I think it makes sense to continue to use a declarative style.

I realize that many work in a team, however, so for you I have no real answers. I still believe that you yourself are the most likely person to revisit code that you wrote. Do it in a way that you feel is understandable, but get it done. Don't fret over how someone else will use it later. When the time comes, they will find a way to do so.

Monday, August 9, 2010

How I Understand Generics.

I have come to realize that my understanding of generics is likely more limiting than I'd care to admit. No better way to address this concern than to try and lay it out.

To start, I must fall back to what I understand of types in Java. Before generics, to define a new type was to define a new class. So, if you had a class:



public class List {
public Object get(int index) {...}
public void add(Object o) {...}
}


Then you have declared a type that is very understandable (helped by virtue of being familiar). Now, generics often start by showing casting happening at the use site of this class. However, I feel it is more revealing to think of another type that is similar to the above definition. Consider:



public class StringList {
public String get(int index) {...}
public void add(String o) {...}
}


Now, this is a new type. Further, it is completely unrelated to the previous type, except for the fact that it looks an awful lot like it.

Now, most people will start with the other types that happen to be named in these and try to build a relationship from that. Such that because people see that String extends Object, so StringList must extend ObjectList. But remember, we aren't talking about the types String and Object, we are looking at StringList and plain List. As defined here, this can not happen. Were one to try this, you would find that, were you to drop the set method, suddenly you could make these types related. As such:


public class List {
public Object get(int index) {...}
}

public class StringList extends List {
public String get(int index) {...}
}


Similarly, were one to drop the get methods, the relationship could actually be written the other way, such that List would actually extend StringList:



public class StringList {
public void add(String o) {...}
}

public class List extends StringList {
public void add(Object o) {...}
}


(It occurred to me this morning that I should say that Java doesn't support contravariant method parameters. So... The above compiles, but only because List is defining a new add method. :) )

Using this code, you would see that nothing that is invalid to the type system would have happened, as you simply added a String to a List. Which, since List has an add method that takes an Object, and a String is an Object, we are all clear.


StringList foo = new List()
foo.add("hello");




But, what all does this have to do with generics in Java? Well, as I said at the beginning, until generics, one had to write a new class file to define a new type. With generics, one can simply describe types. Specifically, you can now define all types that share a common description. For us, this description is simply:



public class List<O> {
public void add(O o) {...}
public O get(int index) {...}
}



So, why did I go through the trouble of exploring relationships of these possible types. Well, remember in Java when you describe all possible types that share a common description, you are describing completely unrelated types (in the typically inheritance based view of relationships). This is what is referred to when it is said that generics are invariant. List<String> describes a type akin to StringList, and List<Object> describes plain old List. And, just as StringList is not a related type to List, nor is List<String> related to List<Object>.

But what if we wanted that relationship ability? That is where wildcard captures come into play. Specifically, the first relationship type can be described with the following snippet. Notice the lines that don't compile are the ones that we could not have been able to code were we writing the class file ourselves:


List<? extends Object> foo = new List<String>();
foo = new List<Integer>();
foo = new List<Double>();
Object o = foo.get(0);
foo.add(new Object()); //Fails.


Similarly, the second relationship could be encoded with the wildcard below:



List<? super String> foo = new List<String>();
foo = new List<CharSequence>();
foo = new List<Object>();
foo.add("hello");
String o = foo.get(0); //Fails



That is all well and good, but what if we wanted to describe not just all similarly described types, but also all of their subtypes as well? Since we can identify in the code what restrictions are required to say that StringList extends String (and, conversely List extends StringList), could we not write a class such that we declared the generic to account for this and have the compiler let us know if we write a method that would be incompatible with what we declared as our intention?

Unfortunately, this is not possible in Java. And it is a direct consequence of the final point on generics that one will hear about in java, that it has use site variance declarations. You can not write a class that describes all possible subtypes of the types it describes, but one can write a variable that defines an inheritance chain. And, in so doing, the compiler will remove methods that would not be possible in the inheritance chain that you declared.