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!”

F# Snippet: sequence partitioning

This F# Snippet illustrates a way to divide a sequence into a sequence of shorter sequences of a fixed length. The snippet uses list comprehensions.

I don’t like it, because it involves flipping through the original sequence over and over and over.

With recursion, we can do all the construction in one pass through the original list. (Yes, I have narrowed the scope from Seq to List. The snippet requires a finite-length sequence so this is not a loss.)

here’s a simple implementation using a tail-recursive method to divide the list into groups of a fixed size.

// tail recursion
let rec build groupSize items outputSoFar currentGroup currentCount =
match items with
| [] -> outputSoFar @ [currentGroup]
| head :: tail when currentCount = groupSize -> build groupSize tail (outputSoFar@[currentGroup]) [head] 1
| head :: tail -> build groupSize tail outputSoFar (currentGroup@[head]) (currentCount + 1)

let buildGroups size items = build size items [] [] 0

buildGroups 2 [1..11] ;;

val it : int list list = [[1; 2]; [3; 4]; [5; 6]; [7; 8]; [9; 10]; [11]]

Of course, this is really a fold operation. Using List.fold ensures we really are tail-recursive.


// same thing, using List.fold
let buildWithFoldFunction groupsize (outputSoFar, currentGroup,currentCount) item =
if (currentCount = groupsize) then
(outputSoFar@[currentGroup]), [item], 1
else
outputSoFar, (currentGroup@[item]), (currentCount + 1)

let buildWithFold groupSize items =
let (output,lastGroup,_) = List.fold (buildWithFoldFunction groupSize) ([],[],0) items
output@[lastGroup]

buildWithFold 2 [1..11]

… but this isn’t really any better than the list-comprehension Snippet, because all those append operators (the @ signs) are causing copies of the original list to be made over and over.

Really, we need to start from the back of the input list. That way we can build the output list-of-lists from the back, using the cons operator, which is efficient. We can use foldBack:


// Now, using cons and foldBack
let buildWithFoldBack groupSize items =
let buildWithFoldBackFunction groupsize item (outputSoFar, currentGroup,currentCount) =
if (currentCount = groupsize) then
(currentGroup :: outputSoFar), [item], 1
else
outputSoFar, (item :: currentGroup), (currentCount + 1)
let lengthOfLastGroup = List.length items % groupSize
let imaginaryElementsInLastGroup = if lengthOfLastGroup = 0 then 0 else groupSize - lengthOfLastGroup
let (output,_,_) = List.foldBack (buildWithFoldBackFunction groupSize) items ([],[],imaginaryElementsInLastGroup) ;
output

let thingWithFoldBack = buildWithFoldBack 3 [1..11];;

val thingWithFoldBack : int list list = [[4; 5; 6]; [7; 8; 9]; [10; 11]]

The interesting bit there is how long that last list should be (the one with the leftovers). If the input list does not divide evenly by the group size, then we change the initial value of that accumulator piece — we pretend that there are already a few items in the last list, so the function that builds output will make that one short.

The fact that I feel clever for doing it that way is a warning sign: clever code is hard-to-read code. However, to someone used to working in a functional style, this would probably make perfect sense.

*For math people: I would prefer to do a second mod operation to determine the imaginaryElementsInLastGroup, but it seemed even more obfuscated: let imaginaryElementsInLastGroup = (groupSize – (items.Length % groupSize)) % groupSize

That’s kinda fun. It’s a little illustration of how functional programming can be efficient and short if we think differently.

A small correction: F# active patterns

In Professional F# 2.0, an example is given for using an active pattern to match against a struct. The example could be improved.

The original:


[]
type Point(x : int, y : int) =
member pt.X = x
member pt.Y = y
override pt.ToString() =
System.String.Format("({0},{1})", x, y)

let newPoint = new Point(0,0)

let (|Point|) (x : int, y : int) (inPt : Point) =
inPt.X = x && inPt.Y = y

let message =
match newPoint with
| Point(0, 0) true -> "You're at the origin!"
| Point(12, 12) true -> "You're at 12,12!"
| _ -> "Who knows where you are?"

System.Console.WriteLine(message) // output: "You're at the origin!"

Here, the active recognizer Point takes two arguments: the desired x and desired y value. The output is a boolean, which is then matched against the pattern “true” in each case.

This would be slightly more succinct and far more flexible if the active recognizer didn’t try to do comparisons, and simply broke down the Point into its components.


type Point( x : int, y: int) =
member pt.X = x
member pt.Y = y

let newPoint = Point(0,12)

let (|Point|) (inPt : Point) =
(inPt.X, inPt.Y)

let message = match newPoint with
| Point(0,0) -> "You are at the origin"
| Point(0,_) -> "You are on the y axis"
| Point(_,0) -> "You are on the x axis"
| Point(x,y) when x = y -> "You are on the line x = y"
| Point(x,y) -> "you are at (" + (string x) + "," + (string y) + ")"

printfn "%s" message // output: "You are on the y axis"

Here, the active recognizer has fewer arguments and returns the int*int tuple representation of a Point. This tuple is then matched against (0,0) or whatever location we care about.

This is more succinct because we don’t have to put “true” at the end of each pattern. It is more flexible because we can use a wildcard (underscore) for either the X or Y property of the point. Even better, we can put a variable name in for either or both, and then that variable can be used in the guard clause (“when x = y”) or the result expression.

For the record, I post this here at the suggestion of an author. Why submit an idea privately when people volunteer for public ridicule?