Explicit Static Types are not for the Compiler, but for the Developer - Duh
Published November 5th, 2007 in Beautiful Java, Dynamic, Ruby, Type Inference, TypesLots and lots of people lament the explicit type system in Java, either they are dynamic ducks or inference intellectuals. Taken from the Boo manifesto:
“Nothing more tiresome than writing the same type name over and over just to make the compiler happy. I’m not talking against static typing in general (boo is statically typed), I’m talking against being coerced by a tyrannical compiler to say things it should already know.”
Beside the fact that my IDE does the typing with auto completion - of course the compiler knows - DUH. But the developer does not know. The developer who wrote the code does know, for about a month. When not working with the code for some time or another developer taking over - then is where the problem rises it’s head. Explicit Static Types are not for the compiler, but for the developer.
def person = Factory.getPerson()
Is person of class Person? Or perhaps Student or Employee? I’m wading through legacy code and the first thing that gets out is correct naming. What once was a person soon gets a Human. How should I know? Explicit typing is writing documentation. Not helping the compiler.
Some people claim explicit types increase lines of code. I can’t see how:
def person = new Person()
versus
Person person = new Person()
There are other things in Java that increase lines of code. Not explicit types. If I had some whishes free, I’d opt for properties and getting rid of unnecessary getter and setters, I’d like to have constructors with named parameters like in Groovy, I want syntactic sugar like [] and [:] for list and map creation, I want all attributes public unless declared private, all methods public unless declared otherwise, I want to drop the “;”, remove unneeded (cast)s where I declared the type explicitly and I wish for closures. But drop explicit types? NO!
My style of programming uses lots of interfaces. A class usually has several small ones and I try to narrow down a class to the interface I use. Narrowing down an object reduces coupling and increases reusability.
public class Person implements Nameable {
private String name;
public String getName() {
return this.name;
};
}
...
Nameable nameable = new Person();
printName( nameable );
Can I do this with type inference? What about
List persons = new ArrayList();
I guess type inference doesn’t help you here, coding against an interface and coding against more abstract entities as proposed by Robert C. Martin is out of the window. With type inference and without explicit types your code will depend on the concrete implementations, not the abstract definitions (or not depend on anything, which depends on your view ;-) Explicit typing is about reducing scope, knowledge and coupling.
Conclusion: Explicit types help the developer and create better, more understandable, more readable and more reusable code.
Sidenote to ducks: People claim that while they need 95% test coverage for dynamic code to find typo bugs, with explicit types the compiler gets the bugs for them:
“We discovered that anything shy of 95% code coverage with Rails means that type-os turn into runtime failures. We do not have any code coverage metrics for the lift code, but we have seen only 1 defect that’s been checked in in the 2 weeks since we started using lift.”
Thanks for listening.
Update: Jari wrote something about types inference, scala and IDE annotations on his blog.
“Beside the fact that my IDE does the typing with auto completion”
Having completion doesn’t make the extra code worth having, it just reduces the number of finger presses to make it appear.
“When not working with the code for some time or another developer taking over - then is where the problem rises it’s head.”
The same IDE-using developer is certainly able to glance at the original method to see what types it has, and maybe just hover over the variable declaration to see what type the inference has given it.
“def person = Factory.getPerson()
Is person of class Person? Or perhaps Student or Employee? I’m wading through legacy code and the first thing that gets out is correct naming.”
person is a fairly meaningless variable name. You’re actually duplicating the type in the name, when you probably would be better off picking a better name, or getting rid of the name. Any time you rename a class, even in Java, you can end up with this kind of variable name becoming out of sync (IDEs can help in simple cases).
“remove unneeded (cast)s where I declared the type explicitly”
Where do you need such a cast? I’d guess most of those are misuse or lack of use of generics.
“Nameable nameable = new Person();
printName( nameable );
Can I do this with type inference?”
Yes. There’s no logic that says that type inference must always pick the most concrete type. However, as there’s no difference in functionality in your above code between you using Nameable or Person, it doesn’t matter much which is picked.
About your ArrayList example in particular, ArrayList is an example of bad API, as it exposes a List implementation by name, instead of just providing a method that gives you a List (and the actual implementation would be private/anonymous). In fact, as I’ve never adapted any code from using ArrayList to using some other implementation, I’d go so far as to say for that example, using ArrayList as the variable type isn’t insane.
“Explicit typing is about reducing scope, knowledge and coupling”
Explicit typing increases coupling by name, simply by making you repeat names. IDEs or other language-aware tools can give you everything that explicit typing has, but in a type-inferred environment.
The usual case in inference is that you can override the compiler’s choices if you like, so languages with inference only *allow* you to elide types, they don’t force you to. A step up on the ladder.
Static types are documentation, yes. That’s still no reason to write them yourself. Your IDE could also tell you the types of things inline. On the other hand, I think declaring the type at the top of a function is good practice, so your compiler can better pinpoint _where_ your type error is when you get something wrong.
“The same IDE-using developer is certainly able to glance at the original method to see what types it has, and maybe just hover over the variable declaration to see what type the inference has given it.”
public String createIdentifiier(id);
Could you tell me the type of id?
def getPerson() {
return Factory.getPerson();
}
Could you tell me the return type? I can’t. And the call chain could be much more complex so it would take more and more time to just check the types. And decide for the correct subtype or interface of the return type.
“person is a fairly meaningless variable name. You’re actually duplicating the type in the name, when you probably would be better off picking a better name, or getting rid of the name”
So, what is a better name? Current? I can’t see how
def current = Factory.getPerson()
makes anything more clear.
Peace
-stephan
@Ricky: I thought about your comment for some time, perhaps you’re right and I should not car what getCurrent() returns, as long as it’s typed and the compiler (and my IDE) know what type it is.
And perhaps you’ve got me wrong, I’m not against type inference. I wish Java was more clever with types. But I’m against dropping explicit types. I often see this with dynamic code:
or
where people add the type in a comment to document their code.
@Greg: “Your IDE could also tell you the types of things inline.”
The IDE can’t decide which type I want.
This is just some kind of design by contract, where I have a type contract for my code. The explicit declaration of what my code expects, in this case a Nameable.
does not declare such a contract for my code. The code following this line has no declared expectations about the nameable variable.
Well, in a language such as Scala, if you really need to narrow the type to Nameable, it’s not a problem:
def nameable: Nameable = new Person()
I for one *do* think that type declarations are redundant when they can be inferred. A smart IDE, working in conjunction with an incremental compiler (a la Eclipse), could just decorate the code view with the inferred type of the member you’re looking at, e.g.
[Person] def person = getPerson
i.e. show the type of the member without the developer having to type it down redundantly. WDYT?
@Jari: Good to know that I could do : Nameable in Scala, one of my side projects is looking into lift.
A smart IDE should know about the type, you’re right. But someone reading source outside an IDE, browsing through a repository with FishEye, using diff tools etc. will not benefit from the IDE knowledge. *Doc tools will also be much harder to write, which could lead to less documentation because the tool is not smart enough.
But I’m with you mostly, an IDE should hide or remove parts (existing things like generics, public modifiers come to mind) of code and just show them when you need them. On the other hand, when the IDE hides code and shows annotations, it’s not important anymore for the developer if the code is there or not (in real, in source). If I can hide the type, I do not care if I have added it with auto completion. Auto completion could just add the type for you without any typing just by type inference :-). And then I don’t care if the source has the type or not.
“Is person of class Person? Or perhaps Student or Employee?”
In most cases the answer is: Who cares?
If I want to know it I move my mouse cursor above “person” and I’ll see a popup-window that tells me what exact type “person” has.
“Explicit typing is writing documentation.”
IMO documenting things I’m not interested in. Why should I care whether it’s a “Person” or a “Nameable” as long as I can call “printName” with it? If I can’t the compiler will tell me why I can’t.
When I see code like
def nameable = new Person();
printName(nameable);
I directly understand what it does.
I know that it fullfills the “contract” needed by “printName” as I wouldn’t be able to compile it otherwise.
If whatever nameable is does not fullfill the contract the compiler will tell me what’s wrong.
In my experience this works perfectly in other languages with type inference so I don’t see why it shouldn’t in Boo (with ‘I never used).
“If I want to know it I move my mouse cursor above “person” and I’ll see a popup-window that tells me what exact type “person” has.”
How is your IDE accomplishing that I wonder without knowing the type.