Saturday, February 15, 2020

Mary had a little lambda

Java 8 brought a lot of changes to the imperative/object oriented world of corporate software development.  So many changes that to this day the more fluent and functional style of code this enables is still not as prevalent as one might hope.

Today I'm going to discuss one aspect of these changes, lambda expressions.

What is a lambda?  It's a function without a name, and with an initially odd looking syntax.

When you see something that looks like this:
.map(t->t.getModels())

You may get a bit confused.  I know I did, and it took me a little while to comprehend this structure.

But really, it's just creating a method call with slightly different syntax.  When written with a bit of the fluff left in it may start to look rather familiar:

map((t)->t.getModels())

Or
map((t)->{ return t.getModels(); })

These all do the same thing.  This is a method, one designed to do only one thing, return the models contained within the parameter object passed to it.  The big difference is that there's no name for the method and we've added a little '->' to indicate that it's not to be executed now but at need.  That is what makes it a lambda, mostly.  It is a method or function, but one which can be passed around to whoever might be interested in it, but whether or not it runs depends on if the program needs results from it.  

Note that you would probably never write this as a stand-alone method.  You already have a one-liner in whatever 't' represents.  

And that "whatever 't' represents" is one of the issues that can make it a little harder than it has to be to understand.  I don't love one letter variable names, so maybe it would be more straightforward to write it like this:

.map(manufacturer->manufacturer.getModels())

I haven't seen that done very often, but I'm going to go this way for my own work because it expresses the intention with perfect clarity.  Sure, in IntelliJ I get hints along the right side of the screen that inform me about types, but what about a code snippet from GitLab or something?

There is one additional thing you absolutely must understand about lambda expressions:  Everything you hand to one has to remain unchanged until the thing actually executes.  If you try to put a mutable variable in there you're gonna have a bad time.  Only the parameter(s) can be different from call to call.  

Finally, there can be a temptation (especially given the syntax of that third example) to stick a few more lines of code inside the brackets.  Don't do that.  If it is a unit or work, it's worth naming it and making it available to others.  Extract a method and call that instead.  I almost wish that we couldn't write things that way, because programmers tend to be...  Oh, let's say expedient about getting things done.  I completely understand that, but please don't do this:

.map(manufacturer-> {
 if (manufacturer.getName.equals("blahblah") {
   doSomething();
 } else {
   doSomethingElse();
 }
 logger.info("Hey, look, I'm screwing up lambdas for all!");
 return manufacturer.stream()
 .collect(asList(manufacturer.getModels()));
 }

Just don't.  If it deserves curly braces, it deserves a name. 

No comments:

Post a Comment