Thursday, March 1, 2012

Strong Typing in Java: a religious argument

Strongly-typed, type-inferred languages like F#, Scala, and Haskell make Java feel like its static typing system is half-assed. This is not Java's fault entirely; it's the way we use it.

Say we have a domain class like

public class User {

public User(String firstName, String lastName, String email, long balanceInDollars) {}

...
}

I'm in agreement with Stephan that using String and primitive types here is wimping out. That is weak typing. Weak! Chuck Norris does not approve!

firstName and lastName and email are conceptually different. If each has their own type, then we get real compile-time type checking.

public class User {

public User(FirstName firstName, LastName lastName, EmailAddress email, Money balance) {}

...
}


This gives us type safety. As we pass a FirstName or an EmailAddress from class to class, we know what we're getting. If we mix up the order of the arguments, we hear about it from the compiler.

"But they're just strings!" you say. "Don't make a bunch of cruft - just call them what they are!"

NO! I say. They are not Strings. They are stored as strings. That, my friend, is an implementation detail. FirstName and EmailAddress represent distinct concepts, and they should have distinct types. The first part of wisdom is calling things by their right names.

There are other OO-style benefits from this, such as putting the validation for each type in its type class and changing the internal representation of the type without affecting its interface. Those may be significant in some situations, but in my argument they're icing. The benefit of strong typing is compile-time checking, and that's reason enough to call a FirstName a FirstName and not a vague String.

Now, let's address this "bunch of cruft" argument. No question, Java does not make this kind of type-wrapping pretty. In Haskell, it takes a one-line type alias. By OO principles, we ought to be able to inherit from String to get its behavior. But noooo, this is Java, and String is final, so we wind up with

public class FirstName {
public final String stringValue;

public FirstName(final String value) {
this.stringValue = value;
}

public String toString() {...}
public boolean equals() {...}
public int hashCode() {...}

}

(notice that I used a *gasp* public field. That's another religious argument for another post. I include it here just to stir people up even further.)

Then every time we want a user:

User u = new User(new FirstName("Joe"), new LastName("Frank"), new EmailAddress("..."), new Money(30));


We can get a little better by providing static methods and statically importing them:

(in FirstName)

public static FirstName(final String value) {
return new FirstName(value);
}


User u = new User(FirstName("Joe"), LastName("Frank"), EmailAddress("..."), Money(30));

That's a little better. But now we get into the part where user is an immutable value type, and we want to update one field.

User updated = new User("Joseph", old.getLastName(), old.getEmailAddress(), old.getBalance());

Ugh! and every time we add a new field to user, even though we simply want to copy it, we have to modify this code.
Copy constructors don't help when the type is immutable.

Let's talk about F# for a minute. F# has records:

type User = { firstName : FirstName; lastName : LastName ; email : EmailAddress; balance : Money }

and then the cool bit is, when you want another record that's almost the same:

let updated = { oldUser with firstName = FirstName("Joseph") }

I want to do this with my Java user. I want to say

User updated = newUser.with(FirstName("Joseph"));

which is cool and all; with strong typing we can overload the "with" method all day long. We can chain them. We can add implementations of "with" for common combinations of fields to reduce instantiations.

(in User)

public User with(FirstName firstName) {
if (this.firstName.equals(firstName)) {
return this; // avoid instantiating an identical value object
}
return new User(firstName, this.lastName, this.email, this.balance);
}

Now you have a whole ton of "with" methods that can be chained. If you add a new field to User, you need to change all of them, but they're all in the same place so that's just fine. What changes together, stays together.

Now, if you don't like an instantiation per changed field, or if you don't like all those "with" methods cluttering up your user class, here's another idea:

User updatedUser = new UserBuilder(oldUser).with(FirstName("Joseph").with(LastName("Frankenfurter").build();

where the UserBuilder keeps the oldUser and uses its values for any fields that aren't provided by the caller to build the new user. That's one instantiation, only one method that instantiates a user, and it's encapsulated into one builder class.

Some people may argue that immutable types and strong typing in Java is going against the way the language is intended to be used, and therefore does nothing but make our lives more difficult. "That's why God gave us POJOs," they say. Java is a powerful language, and it is capable of supporting more idioms than the ones the language designers envisioned. We can grow as programmers within our language of choice. Java supports strong typing.

Strong typing gives us compiler errors on what would otherwise be caught only during testing. It creates some extra code, sure, but it's localized. It can make working with immutable types a little cleaner.

I say, never use "String!"

52 comments:

  1. Cool post. You could also argue that if you're in a place where the devs are progressive enough to even consider such an idea, you might as well just switch to Scala :-)

    ReplyDelete
  2. Great post! Just yesterday I was contemplating a similar builder approach to constructing SQL statements out of string parts.

    ReplyDelete
  3. It gives you more compile time safety at the cost of extra runtime work. I wish Java either had a typedef-like compile-time mechanism or a JIT strategy for rearranging data structures at runtime.

    ReplyDelete
  4. In Scala, you can use "tagged types" which gives you compile-type safety while using the underlying String or (boxed) primitive at runtime:

    http://etorreborre.blogspot.com/2011/11/practical-uses-for-unboxed-tagged-types.html

    You would have something like:

    type FirstName = String @@ FirstNameTag
    type LastName = String @@ LastNameTag
    type EMailAddress = String @@ EMailAddressTag
    type Money = Int @@ MoneyTag

    class User(
    val firstName:FirstName,
    val lastName:LastName,
    val emailAddress:EMailAddress,
    val money:Money
    )

    With some helpers to cast into the tagged types:

    def firstName(value:String) = value.asInstanceOf[FirstName];
    def lastName(value:String) = value.asInstanceOf[LastName];
    def emailAddress(value:String) = value.asInstanceOf[EMailAddress];
    def money(value:Int) = value.asInstanceOf[Money];

    val user = new User(
    firstName("Joe"),
    lastName("Bagofdonuts"),
    emailAddress("joebagofdonuts@foo.com"),
    money(100)
    )

    Implicit conversions could be used for the casting, but there would be trade-offs regarding type safety since untagged types would get implicitly converted and you might not want that happen.

    In Scala 2.9.1, there is some issues with using them in case classes which would be preferable in the User example above. When those issues get ironed out, this could be an interesting technique for those who like stronger type safety.

    ReplyDelete
    Replies
    1. Also, I think value classes from SIP-15 (http://docs.scala-lang.org/sips/pending/value-classes.html) are interesting, when it hits mainline. Value classes may deprecate the tagged type idiom (although I haven't thought through a comprehensive comparison).

      Delete
  5. Tim, that's perfect, thank you! I was wondering how to best implement the same ideas in Scala.

    Nayan -- we are, indeed, considering switching to Scala.

    ReplyDelete
  6. Some things:
    1) Avro uses builders and it makes for a really nice programming environment.
    2) I write posts which are 'this is good - that is bad' but I never really 100% agree with them (my own, or other peoples). I like the domain object model but am loath to say 'never' use string. There are places where it is just going too far to define everything as its own object. You have actually done this; you do not use an abstract Name type for example.
    3) Immutability is becoming more and more in line with the design of the JVM and so cannot be considered to be against the idiom of Java. If objects are immutable then there is a better chance the JVM will be able to remove them from the heap using escape analysis (for example)

    ReplyDelete
    Replies
    1. Alexander - #2, I agree completely. Posts like this are an exploration of a principle. In real life, being aware of both extremes lets us choose intelligently where in the middle is optimal for each application. If correctness were paramount, I'd use strong typing all the time, but in real life, efficiency and simplicity matter too.
      #3, that's good news!

      Thank you for this input.

      Delete
  7. I agree, all (or at least most) systems should be build without primitive types. But you don't need to switch to Scala for this, it can be done perfectly well using Java.

    The system I work on has approx. 240 entities and 340 immutable value-type classes (Java with JPA). The BigDecimal valuetypes handle rounding, scaling and precision issues. I can not add an 'Amount' to a 'Price' unless I explicitly convert, which is a good thing. I can not provide a 'Name' where a 'Reference' is required as they are different concepts (and have different lengths). I can write getVatPercentage().ofAmount(getInvoiceAmountExt()) because value type 'Percentage' has extra methods. I can validate a value type on instantiation so I only ever have valid values. I can make type hierarchies where 'SalesAmount' is-a 'Amount'. Etc. etc...

    The business logic is simpler and more expressive and the compiler will tell you when you mix up concepts.

    By using code-generation for entities and value-types the extra effort is minimal. Why are we not all doing this... I think for most systems the benifits outweigh the performance loss by far.

    ReplyDelete
    Replies
    1. Marc, this is inspiring! Thank you.

      Delete
  8. Could I request a Name class instead of a FirstName and LastName pair? It won't be long before someone wishes to store their MiddleName and SecondMiddleName!

    ReplyDelete
    Replies
    1. Of course! As long as Name contains a FirstName and LastName, rather than Strings. :-)

      Delete
  9. I've had this idea for a while; why not use the static type system to support data semantics? It's great to read someone else had the idea too.

    Spend some time while with a dynamic language and you start to wonder what the benefit of the static type system really is, other then reducing the most gross of mistakes in a project with developers who don't communicate or pay attention.

    Since most classes aren't as much types as mere namespaces, why not give all domain data it's own type, not just the aggregates?

    ReplyDelete
  10. One neat thing about being the first company to purchase the trucks is that Robichaud is pretty much involved in any design changes that may be made to the vehicle. additional info read

    ReplyDelete
  11. Since most classes aren't as much types as mere namespaces, why not give all domain data it's own type, not just the aggregates. more learn

    ReplyDelete
  12. For this problem greedy algorithm is good: Set_cover_problem . Idea is that, for some permutation of input, greedy algorithm will return optimal solution. the venus factor does it work

    ReplyDelete
  13. One nice thing about being the first company to buy the vehicles is that Robichaud is fairly much engaged in any style changes that may be made to your vehicle. recently published

    ReplyDelete
  14. Less than per 7 days later, LG&E declared possible programs to stage out the losing of fossil fuel at the place by 2016 and modify to organic gas, stating stronger government rules on pollutants. Click hear to see examples

    ReplyDelete
  15. The next community meeting in Bedford will likely be organised in beginning 2012, and Little is already getting ready for the fight. which Stephen Williams will preside over

    ReplyDelete
  16. According to Morgan Stanley, copper costs will enhance 7.6% in 2013 from last year as requirement in Chinese suppliers, the U.S. and even European countries is prediction to improve amongst a provide lack for the steel. through his guidance

    ReplyDelete
  17. She tends to be soft-spoken and keep her go down, targeted on her notices. But once she gets going, her concept is highly effective. http://gspm.gwu.edu/darius-anderso

    ReplyDelete
  18. Moreover to monitoring moment-to-moment threats such as an beginning car or a decrease banister, our threat verifying starts to intuit a distant but progressively approaching dark thinking — the approaching end, the biggest boundary. Igor Cornelsen

    ReplyDelete
  19. But nonetheless, wasn't the whole point of intoducing interest on reserves that the fed could tighten without selling any of the securities it holds? Especially since the mortgage bonds might well end up worthless. Sultan Alhokair - Profile

    ReplyDelete
  20. Thanks to an wide range of mobile cellphone programs and thinking handling, companies can often find out out most of the facts they need, and handle their day, without ever speaking with the house company office. https://www.facebook.com/JmhJasonHalpern?ref=stream

    ReplyDelete
  21. The Technological Town Hall” at AHR Jan 28 at 1:00 p.m.Indicate has a success of experience with preparing company activities and he has hand to make AHR the effective display that it is today.I lately taken up with Indicate Halligan, CEO of H+A Globally, Chicago, illinois, il, about the very subject of company activities. contacting Stansberry and Associates

    ReplyDelete
  22. Stay at the impressive of new technological innovation – make sure you have educational sessions and members introducing considers like geothermal power energy, biomass heat power, etc. http://bits.blogs.nytimes.com/2013/11/20/a-gift-from-steve-jobs-returns-home/?_php=true&_type=blogs&_r=0

    ReplyDelete
  23. It is our objective to generate the first Power Van for 90 times and find out all of the technicalities that might come with an EV or if there are any style changes we would like to have involved with upcoming vehicles,” said Robichaud. Lee G. Lovett

    ReplyDelete
  24. Disappointed that it did not include a miter guide. I have even made adjustments so I can throw in a little ground flavored coffee while still using the grinder. bmw repair shops

    ReplyDelete
  25. It is then our objective to begin the conversion at two monthly. Logistically it needs a chance to exchange out an experienced to a new van and we do not want to overcome ourselves, although the earlier we change the earlier we begin preserving. when he created that

    ReplyDelete
  26. Thanks to an wide variety of mobile cellphone programs and thinking processing, organizations can often find out out most of the facts they need, and handle their day, without ever discussing with the house organization office. truth about cellulite

    ReplyDelete
  27. Boulder Electric Vehicle and a Precision customer, invented the electric service truck, and convinced Robichaud to test-drive the truck that he felt would be a perfect fit for the service industry because of the short routes service technicians drive daily. old school new body

    ReplyDelete
  28. Often it is the surprising lack of way of life of someone near that provides this home; and then the frequency of hospital visits and memorials progressively starts to select up amount, like a drumbeat in the woodlands. visit site

    ReplyDelete
  29. Boulder Electric Vehicle and a Precision customer, invented the electric service truck, and convinced Robichaud to test-drive the truck that he felt would be a perfect fit for the service industry because of the short routes service technicians drive daily. click to read

    ReplyDelete
  30. I've already started using some of them. There wasnt a major growth but my boobs were a little more plump and full. Disappointed that it did not include a miter guide. I have even made adjustments so I can throw in a little ground flavored coffee while still using the grinder. old school new body

    ReplyDelete
  31. A excellent very good example is AHR Expo, the world’s biggest HVACR-focused display and meeting. The 2012 event in Chicago, illinois last Jan set an all-time record of 60,000 participants and more than 2,000 participants. go to website

    ReplyDelete
  32. A aspect this query I would like to pressure that you never evaluate the development of cash by the FED with the US GDP and its development. important link

    ReplyDelete
  33. Precisely by what means will an occasion to the marvelous isle of the Dominican Republic adjust from those in regards to distinctive Caribbean goals? In the first place, the Dominican Republic is not just a minor island like various these other Caribbean nations. venus factor

    ReplyDelete
  34. As almost everyone’s expenses knowledgeable, many organizations had to cut back on their display participation and the wide variety of individuals they sent to be existing at activities was considerably reduced. Consequently, some reveals stopped to are available or became much smaller versions of their halcyon periods. Saran Wrap Weight Loss

    ReplyDelete
  35. After receiving a home evaluation for the real value of your home, industry the residence and get in touch with a title organization to help with the documentation. Sell My Home

    ReplyDelete
  36. Even so, conventional dealing is a lot more focused, there are more suppliers provided and seem to be far more complete in evaluation to that of the distribute gambling itself. Taruhan Bola

    ReplyDelete
  37. To talk about particular item or set up difficulties with manufacturers’ technical employees.To talk about particular item or set up difficulties with manufacturers’ technical employees.To talk about particular item or set up difficulties with manufacturers’ technical employees. click to find out more

    ReplyDelete
  38. I've already started using some of them. There wasnt a major growth but my boobs were a little more plump and full. Disappointed that it did not include a miter guide. I have even made adjustments so I can throw in a little ground flavored coffee while still using the grinder. Quick House Sales

    ReplyDelete
  39. This really is because of the improved bloating inside the tummy (a condition just like edema). This can result in other complications like improved stress on a back, leading to serious back pain. her explanation

    ReplyDelete
  40. Moreover to monitoring moment-to-moment threats such as an beginning car or a decrease banister, our threat verifying starts to intuit a distant but progressively approaching dark thinking — the approaching end, the biggest boundary. Nitro Shred

    ReplyDelete
  41. You can understand a lot about the material of a toy by reading the brand. Choose non-toxic. All of our wood created toys and games are non-toxic, cause 100 % free and created with natural completes. address

    ReplyDelete
  42. I like this. It is a good trick. If the file is a PDF then you can overtype in a similar way with Nitro PDF Reader. It is a good, free alternative to Adobe Reader and is not targetted by malware (yet).
    typind and transcribtive services

    ReplyDelete
  43. Nice post! the way of explanation is so good and be informative. You can improve your typing speed by using different typing services online.

    ReplyDelete
  44. CRX, so I need an easy way to map my prod­uct node in CRX to my Prod­uct class in Java. This is where the Adapter­Fac­tory comes in. read more

    ReplyDelete
  45. This comment has been removed by the author.

    ReplyDelete
  46. This comment has been removed by a blog administrator.

    ReplyDelete