From the archive: Originally written in January 2010, this post from my old blog, Graham Hacking Scala, has been consistently popular ever since and I thought it deserved a new lease on life here…
Java at work: an increasingly frustrating experience
A colleague and I were writing a test in Java today and I had one of those “I can’t believe it takes this much work to write this stupid function” moments. If you do any Scala programming but still do a lot of Java programming, you’ll know exactly what I mean. I’ll walk you through a mock of what we were doing and you can see why Scala just pummels Java when it comes to expressiveness. (Early warning: do not use the word “expressive” when trying to convince your manager to let you use Scala at work.)
So, here’s the gist of the code that we were testing:
import java.util.ArrayList; import java.util.List; class AccountSummaryService { public List<CustomerSummaryDTO> getAccountSummaries() { return new ArrayList<CustomerSummaryDTO>(); } } class CustomerSummaryDTO { private String name; private List<AccountSummaryDTO> accountSummaries; public String getName() { return name; } public List<AccountSummaryDTO> getAccounts() { return accountSummaries; } } class AccountSummaryDTO { private String name; private int balance; public String getName() { return name; } public int getBalance() { return balance; } }
and here’s the gist of the test that we wrote:
import org.junit.Test; import java.util.List; import java.util.NoSuchElementException; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; class AccountSummaryServiceJavaTest { AccountSummaryService service = new AccountSummaryService(); @Test public void getAccountSummaries() { List<CustomerSummaryDTO> result = service.getAccountSummaries(); assertThat(balanceFor("Customer 1", "Account #2", result), is(1000000)); } private int balanceFor(String customerName, String accountName, List<CustomerSummaryDTO> result) { for (CustomerSummaryDTO customer : result) { if (customer.getName().equals(customerName)) { for (AccountSummaryDTO account : customer.getAccounts()) { if (account.getName().equals(accountName)) { return account.getBalance(); } } } } throw new NoSuchElementException("Account not found"); } }
As you can see, I’m a bit of a fan of fluent interfaces, hence the use of Hamcrest matchers and the balanceFor()
method. This makes for a nice, readable test, but boy is that balanceFor()
method ugly! Considering the simple job it’s doing, there is a lot of code and – the thing I especially hate – a lot of noise. I would say this code has a relatively low signal-to-noise ratio.
The lack of visual appeal isn’t the only problem. Another problem is that, despite being trivial, it took more than a few seconds to write. An even more disturbing problem is that my colleague and I both spent another 30 seconds triple-checking this code to make sure it was doing what we wanted it to. Not because it’s complex (it’s not) or because we’re stupid (we’re not). We were checking it over because (a) it took too long to write for such a simple operation, so we thought maybe it could be simplified, and (b) there is so much noise in the code that it just LOOKED like a bear trap for stupid errors.
I quipped that the same function in Scala would be much shorter and easier to read. My colleague called me on it and asked what the Scala would look like, so I wrote some hacky Scala in a comment to give him the feel of it. It was, of course, slightly wrong, and turned out to be more complex than it needed to be, but I wrote it again when I got home and compiled it just to prove my point. Here’s the balanceFor()
method rewritten in Scala:
def balanceFor(customerName: String, accountName: String, result: Iterable[CustomerSummaryDTO]) = result.find(_.getName == customerName).get .getAccounts().find(_.getName == accountName).get .getBalance
You can see straight away that this function is shorter. Just syntactically, using Scala has removed a lot of the noise: theres no type declarations in the implementation; there’s no verbose but meaningless local variable names because we can use _ to create anonymous functions; we’re able to use ==
instead of .equals()
with the same effect; the exception is thrown by the API because find()
returns an Option; and, thanks to the power of Scala’s API, we can even lose the braces because the whole function is just one expression.
But its endearing qualities don’t end at the reduced demands on screen lines and hard drive. The Scala implementation reads in a way that is so much closer to the intent of the function – so much closer than a for loop with a nested if, with a nested for, with a nested if, followed by four closing braces and a throw statement. The find()
function expresses what we are trying to do exactly; to a reader attempting to comprehend the code, its simplicity is profound when compared to the for statement and if statement that combined do the same thing but are spread over two lines in the Java version.
To make a fair comparison, I will highlight that Scala has added one piece of noise to the implementation that wasn’t present in the Java: the .get
call at the end of each of the first two lines, used to access the contents of the Option returned by find()
. To anyone who has read an introductory Scala book, this will make immediate sense and to anyone that hasn’t even seen Scala before, a 30-second explanation of Option should make everything pretty clear.
For me, the best part about this code is that the combination of its succinctness, expressiveness and almost total lack of noise means that we wouldn’t need to spend 30 seconds triple-checking it. Not only did it take such a short time to write that I could still remember the first key-stroke by the time I’d finished, but I can look at this code and see what it does without having to read it.
2014 Update!
Of course, being a lot older and a little wiser, I now look at that code above and go… “Urgh!” Everything I’ve written above is true: the code is more succinct and more expressive than the corresponding Java, but it’s far from being idiomatic Scala. Having learnt a lot more about for comprehensions and Options in the interceding years, I’d now probably write that function like this:
def balanceFor(customerName: String, accountName: String, result: Iterable[CustomerSummaryDTO]): Option[Int] = { (for { customer <- result if customer.getName == customerName account <- customer.getAccounts if account.getName == accountName } yield account.getBalance) .headOption }
… and deal with the Option[Int] in the test by expecting a Some(value) or None.
— end of 2014 update.
Hang on, I thought this blog was about using Scala at work?
Actually, so far it’s been about not using Scala at work, and how annoying that can be.
How can we change this situation?
Well, imagine you go to your boss and you say, “Hey, Boss. Can we start using Scala?” and he says, “Why?” and you say, “Um, ’cause it’s a functional language so it’s more expressive and it has monads and everything’s immutable so it might help us to write better multi-threaded code” and he’ll say? … “Get back to work”.
Well, the silly little method above, along with the issues it solved, is a perfect little use case for why Scala could be great for your company’s software development. Forget all the functional paradigms and monadic, higher-kinded academia – these are just buzz words that dumb people use to sound important. (Kidding.) No, there are real, practical, business reasons your company should let you code in Scala. My experience, as demonstrated in the code here, is that Scala lets me write code faster and also makes it harder for me to get things wrong. Or, to rephrase that in managerese, it increases output and improves quality.
Update:
Some people have commented, quite rightly, about the above examples not being Java 8. That’s because this blog was a re-post of an older blog. I’ve written a follow-up with some Java 8 examples of lambdas and a discussion of the differences with Scala.
So, instead of pitching technical buzzwords at your boss, imagine trying this out: “Hey, Boss. How would you like it if I could write twice as much code in a day and have more confidence that it actually does what I intend it to?” Now you’re speaking his language: Output and Quality. But why not go one better than imagining? Why not go and ask your boss now!
When he says yes, you might want to get one of these…
Image credit: ‘Frustrated Rafa‘ by Beth
java 8 equivalent of your example would be identical, no need for Scala…
Thanks for your comment. I don’t think that’s quite right as the Stream API complicates things a bit. Have a look at this post:
https://www.grahamlea.com/2014/08/java-8-lambdas-scala-obsolete/
Pingback: Does Java 8's lambda capability make Scala obsolete?Evolvable Me
Wasn’t Scala an incubating environment to integrate functional features in JAVA 8 ?
I’m sure you’re joking, but if that were the case, the incubation period is disastrously slow: about ten years to commute one functional feature, while not pulling in mountains of other useful stuff. See this post for more info:
https://www.grahamlea.com/2014/08/java-8-lambdas-scala-obsolete/
Oh my goodness! Amazing article dude! Many thanks, However
I am going through troubles with your RSS. I don’t know the reason why I cannot subscribe to it.
Is there anybody having similar RSS issues?
Anyone who knows the answer will you kindly respond?
Thanks!!