+ All Categories
Home > Technology > What You Need to Know About Lambdas - Jamie Allen (Typesafe)

What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Date post: 28-Jan-2015
Category:
Upload: jaxlondonconference
View: 109 times
Download: 1 times
Share this document with a friend
Description:
Presented as a keynote at JAX London 2013 Lambdas are coming to the Java language in the upcoming release of Java 8! While this is generally great news, many Java developers have never experienced Lambdas before, and have not yet learned the best ways to use them for maximum productivity. In this talk, we will discuss best practices for using Lambdas in Java and other JVM-based languages, and we will investigate how we can make these constructs more usable in production.
Popular Tags:
43
Jamie Allen | Typesafe What You Need to Know About Lambdas Wednesday, November 6, 13
Transcript
Page 1: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Jamie Allen | Typesafe

What You Need to Know About Lambdas

Wednesday, November 6, 13

Page 2: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Who Am I?

• Director of Consulting for

• User of lambdas on the JVM for ~5 years

@jamie_allen

Wednesday, November 6, 13

Page 3: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Wednesday, November 6, 13

Page 4: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Wednesday, November 6, 13

Page 5: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Wednesday, November 6, 13

Page 6: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

I Love Functional Programming!

• Functional Programming is:

• Immutability

• Referential Transparency

• Functions as first-class citizens

Wednesday, November 6, 13

Page 7: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

We Want Declarative Codefinal List<Integer> numbers = Arrays.asList(1, 2, 3);

final List<Integer> numbersPlusOne = Collections.emptyList();

for (Integer number : numbers) { final Integer numberPlusOne = number + 1; numbersPlusOne.add(numberPlusOne);}

Wednesday, November 6, 13

Page 8: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

What is a Lambda?

• A function literal

• Not bound to a variable name, can only be used in the context of where it is defined

• Merely one of many possible implementations you can use in Functional Programming

Wednesday, November 6, 13

Page 9: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Java 8import java.util.List;import java.util.Arrays;import java.util.stream.Collectors;

public class LambdaDemo { public static void main(String... args) { final List<Integer> numbers = Arrays.asList(1, 2, 3);

final List<Integer> numbersPlusOne = numbers.stream().map(number -> number + 1). collect(Collectors.toList()); }}

λ

Wednesday, November 6, 13

Page 10: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Nashorn Javascript#!/usr/bin/env jjs -scripting

var result = [];var list = new java.util.ArrayList();list.add(1);list.add(2);list.add(3);list.parallelStream(). map(function(e) e + 1). forEach(function(t) result.push(t));

λ

Wednesday, November 6, 13

Page 11: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Scala

object LambdaDemo extends App { val numbers = List(1, 2, 3) val numbersPlusOne = numbers.map(number => number + 1)}

λ

Wednesday, November 6, 13

Page 12: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Clojure

(ns LambdaDemo.core)(defn -main [& args] (println(map #(+ % 1) [1, 2, 3])))

λ

Wednesday, November 6, 13

Page 13: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

JRuby

require "java"

array = [1, 2, 3]array.collect! do |n| n + 1end

λ

Wednesday, November 6, 13

Page 14: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

What is the Problem?

Wednesday, November 6, 13

Page 15: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

There Are Caveats

Wednesday, November 6, 13

Page 16: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Not Reusable

• Lambdas are limited in scope to their call site

• You cannot reuse the functionality elsewhere

Wednesday, November 6, 13

Page 17: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Not Testable in Isolation

• How can you test code by itself when you have no identifier through which you can call it?

• You can only test them by writing more tests for their enclosing method

Wednesday, November 6, 13

Page 18: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Maintainability• There is nothing inherently descriptive

about a lambda

• Developers have to read through the entire lambda to figure out what it is doing

• The more complex the lambda is, the harder this is to do

• Waste of valuable development time

Wednesday, November 6, 13

Page 19: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Example• In Scala, I sometimes see code like this:

               val  next  =  x.map  {                    case  Success(k)  =>  {                        deriveValueAsynchronously(worker(initValue))(pec).map  {                            case  None  =>  {                                val  remainingWork  =  k(Input.EOF)                                success(remainingWork)                                None                            }                            case  Some(read)  =>  {                                val  nextWork  =  k(Input.El(read))                                Some(nextWork)                            }                        }(dec)                    }                    case  _  =>  {  success(it);  Future.successful(None)  }                }(dec)

}

} }}}

λλλλλ

Wednesday, November 6, 13

Page 20: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Wednesday, November 6, 13

Page 21: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Lousy Stack Traces

• Compilers have to come up with generic names for their representation of lambdas to run on the JVM, called “name mangling”

• The stack trace output tells you very little about where the problem occurred

Wednesday, November 6, 13

Page 22: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Java 8

numbers.stream().map(number -> number / 0)

Exception in thread "main" java.lang.ArithmeticException: / by zero at LambdaDemo.lambda$0(LambdaDemo.java:9) at LambdaDemo$$Lambda$1.apply(Unknown Source) at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:510) at LambdaDemo.main(LambdaDemo.java:9)

wat

Wednesday, November 6, 13

Page 23: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Nashorn Javascript

list.parallelStream(). map(function(e) e / 0)

[1, 2, 3]Infinity,Infinity

Wednesday, November 6, 13

Page 24: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Nashorn Javascript

list.parallelStream(). map(function(e) e / 0)

[1, 2, 3]Infinity,Infinity

Wednesday, November 6, 13

Page 25: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Favorite Tweet Ever

“JavaScript doesn't have a dark side, but it does have a dimly lit room full of angry clowns with rubber mallets.”

- @odetocode, Jan 5, 2010

Wednesday, November 6, 13

Page 26: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Scalaval numbersPlusOne = numbers.map(number => number / 0)

Exception in thread "main" java.lang.ArithmeticException: / by zero at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$$anonfun$1.apply(LambdaPlayground.scala:23) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$$anonfun$1.apply(LambdaPlayground.scala:23) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244) at scala.collection.immutable.Range.foreach(Range.scala:141) at scala.collection.TraversableLike$class.map(TraversableLike.scala:244) at scala.collection.AbstractTraversable.map(Traversable.scala:105) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$delayedInit$body.apply(LambdaPlayground.scala:23) at scala.Function0$class.apply$mcV$sp(Function0.scala:40) at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12) at scala.App$$anonfun$main$1.apply(App.scala:71) at scala.App$$anonfun$main$1.apply(App.scala:71) at scala.collection.immutable.List.foreach(List.scala:318) at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32) at scala.App$class.main(App.scala:71) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$.main(LambdaPlayground.scala:22) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest.main(LambdaPlayground.scala)

wat

Wednesday, November 6, 13

Page 27: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Clojureprintln(map #(/ % 0) [1, 2, 3])))

Exception in thread "main" (java.lang.ArithmeticException: Divide by zero at clojure.lang.Numbers.divide(Numbers.java:156) at clojure.lang.Numbers.divide(Numbers.java:3671) at helloclj.core$_main$fn__10.invoke(core.clj:5) at clojure.core$map$fn__4087.invoke(core.clj:2432) at clojure.lang.LazySeq.sval(LazySeq.java:42) at clojure.lang.LazySeq.seq(LazySeq.java:60) at clojure.lang.RT.seq(RT.java:473) at clojure.core$seq.invoke(core.clj:133) at clojure.core$print_sequential.invoke(core_print.clj:46) at clojure.core$fn__5270.invoke(core_print.clj:140) at clojure.lang.MultiFn.invoke(MultiFn.java:167) at clojure.core$pr_on.invoke(core.clj:3266) at clojure.core$pr.invoke(core.clj:3278) at clojure.lang.AFn.applyToHelper(AFn.java:161) at clojure.lang.RestFn.applyTo(RestFn.java:132) at clojure.core$apply.invoke(core.clj:601) at clojure.core$prn.doInvoke(core.clj:3311) at clojure.lang.RestFn.applyTo(RestFn.java:137) at clojure.core$apply.invoke(core.clj:601) at clojure.core$println.doInvoke(core.clj:3331) at clojure.lang.RestFn.invoke(RestFn.java:408) at helloclj.core$_main.invoke(core.clj:5) at clojure.lang.Var.invoke(Var.java:411) ... at clojure.main.main(main.java:37)

wat

Wednesday, November 6, 13

Page 28: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

JRubyarray.collect! do |n| n / 0

ZeroDivisionError: divided by 0 / at org/jruby/RubyFixnum.java:547 (root) at HelloWorld.rb:11 collect! at org/jruby/RubyArray.java:2385 (root) at HelloWorld.rb:10

not half bad, really

Wednesday, November 6, 13

Page 29: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Difficult Debugging• Debuggers on the JVM can only

disambiguate code at the source line - write your lambdas to leverage this

final List<Integer> numbersPlusOne = numbers.stream(). map(number -> number + 1).collect(Collectors.toList());

NO!

Wednesday, November 6, 13

Page 30: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Digression: Lambdas versus Closures

• In the purest sense, closures are merely lambdas that close over some state from outside of their enclosing scope

final int x = 1;final List<Integer> numbersPlusOne = numbers.stream().map(number -> number + x). collect(Collectors.toList());

Wednesday, November 6, 13

Page 31: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Closing Over State• Lambdas have access to all variables that are in

scope

• It is very easy to “close over” something mutable and cause headaches in multi-threaded code

• Java enforces that values to be closed over are final, but that only affects assignment - you can still change what is INSIDE that variable (like the contents of a collection)

Wednesday, November 6, 13

Page 32: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Solution

We want to maintain our ability to program in a functional style, while having something

maintainable and understandable in production

Wednesday, November 6, 13

Page 33: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Named Functions?

• Seems like it would help, but it depends on the compiler and how it manages the “scope” of that function

• It is possible that stack traces will still not show the name of the function

Wednesday, November 6, 13

Page 34: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Named Function

object LambdaTest extends App { val addOneToValue = (x: Int) => x + 1

val myList = (1 to 20).map(addOneToValue)}

Wednesday, November 6, 13

Page 35: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Named Function Stack Traceval badFunction = (x: Int) => x / 0val myList = (1 to 20).map(badFunction)

Exception in thread "main" java.lang.ArithmeticException: / by zero at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$$anonfun$1.apply$mcII$sp(LambdaPlayground.scala:23) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$$anonfun$2.apply(LambdaPlayground.scala:24) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$$anonfun$2.apply(LambdaPlayground.scala:24) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244) at scala.collection.immutable.Range.foreach(Range.scala:141) at scala.collection.TraversableLike$class.map(TraversableLike.scala:244) at scala.collection.AbstractTraversable.map(Traversable.scala:105) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$delayedInit$body.apply(LambdaPlayground.scala:24) at scala.Function0$class.apply$mcV$sp(Function0.scala:40) at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12) at scala.App$$anonfun$main$1.apply(App.scala:71) at scala.App$$anonfun$main$1.apply(App.scala:71) at scala.collection.immutable.List.foreach(List.scala:318) at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32) at scala.App$class.main(App.scala:71) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$.main(LambdaPlayground.scala:22) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest.main(LambdaPlayground.scala) wat

Wednesday, November 6, 13

Page 36: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

“Lifting” a Method

• We have the ability in Java 8 and Scala to “lift” or coerce a method into a function

• The method must meet the contract of the lambda usage of the compiler, such as only taking one argument representing the input of the function

Wednesday, November 6, 13

Page 37: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Stack Trace of a Methoddef badFunction = (x: Int) => x / 0val myList = (1 to 20).map(badFunction)

Exception in thread "main" java.lang.ArithmeticException: / by zero at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$$anonfun$badFunction$1.apply$mcII$sp(LambdaPlayground.scala:23) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$$anonfun$1.apply(LambdaPlayground.scala:24) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$$anonfun$1.apply(LambdaPlayground.scala:24) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244) at scala.collection.immutable.Range.foreach(Range.scala:141) at scala.collection.TraversableLike$class.map(TraversableLike.scala:244) at scala.collection.AbstractTraversable.map(Traversable.scala:105) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$delayedInit$body.apply(LambdaPlayground.scala:24) at scala.Function0$class.apply$mcV$sp(Function0.scala:40) at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12) at scala.App$$anonfun$main$1.apply(App.scala:71) at scala.App$$anonfun$main$1.apply(App.scala:71) at scala.collection.immutable.List.foreach(List.scala:318) at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32) at scala.App$class.main(App.scala:71) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$.main(LambdaPlayground.scala:22) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest.main(LambdaPlayground.scala)

Better, but why $1

Wednesday, November 6, 13

Page 38: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Digression• You can define your methods like that, but

“def” is not stable - it will reevaluate the right side of the equals sign and return a new but identical function each time!

def badFunction = (x: Int) => x / 0

def badFunction(x: Int) = x / 0

• Better to stick with simple method syntax instead

Wednesday, November 6, 13

Page 39: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Stack Trace of a Stable Methoddef badFunction(x: Int) = x / 0val myList = (1 to 20).map(badFunction)

Exception in thread "main" java.lang.ArithmeticException: / by zero at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$.badFunction(LambdaPlayground.scala:24) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$$anonfun$1.apply(LambdaPlayground.scala:25) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$$anonfun$1.apply(LambdaPlayground.scala:25) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244) at scala.collection.immutable.Range.foreach(Range.scala:141) at scala.collection.TraversableLike$class.map(TraversableLike.scala:244) at scala.collection.AbstractTraversable.map(Traversable.scala:105) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$delayedInit$body.apply(LambdaPlayground.scala:25) at scala.Function0$class.apply$mcV$sp(Function0.scala:40) at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12) at scala.App$$anonfun$main$1.apply(App.scala:71) at scala.App$$anonfun$main$1.apply(App.scala:71) at scala.collection.immutable.List.foreach(List.scala:318) at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32) at scala.App$class.main(App.scala:71) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest$.main(LambdaPlayground.scala:22) at org.jamieallen.effectiveakka.pattern.extra.LambdaTest.main(LambdaPlayground.scala) Perfect!

Wednesday, November 6, 13

Page 40: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Benefits• You can’t close over variables

• Better stack traces

• More debuggable

• More testable

• More maintainable and descriptive

• Reusable

Wednesday, November 6, 13

Page 41: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Rule of Thumb

• Reserve lambda usage for the most basic expressions

• Externalize anything more significant than that to methods

Wednesday, November 6, 13

Page 42: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Language Creators

• Language designers and tool producers need to help us

• At Typesafe, we’re making our Eclipse-based Scala IDE more lambda-friendly with each release

Wednesday, November 6, 13

Page 43: What You Need to Know About Lambdas - Jamie Allen (Typesafe)

Thank You!

• Questions?

Wednesday, November 6, 13


Recommended