@knight_cloud
What You Need to
Know About Lambdas
Ryan Knight
Senior Consultant | Typesafe
Acknowledgement
• Material originally created by Jamie Allen	

• @jamie_allen
• Modified by Ryan Knight	

• @knight_cloud
I Love Functional Programming!
• Functional Programming is:	

• Immutability	

• Referential Transparency	

• Functions as first-class citizens	

• Eliminating side effects
Why Functional Programming?
• Foundation for Reactive Programming	

• Requires Callbacks to handle Events and
Async Results	

• Avoid Inner Classes	

• Higher-Level of Abstraction	

• Define the What not the How
Imperative Code
final 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);
}
We Want Declarative Code
• Remove temporary lists - List<Integer>
numbersPlusOne = Collections.emptyList();	

• Remove looping - for (Integer number : numbers)	

• Focus on what - x+1
What is a Lambda?
• A function literal - fixed value in the code.	

• 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
Java 8 Lambdas
• Functional Interface that defines a Single
Abstract Method	

• Target Type of the Lambda is the Functional
Interface 	

• java.util.function - Package with standard
Functional Interfaces
Java 8 Function Example
@FunctionalInterface
public interface Function<T, R> {
!
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
Target Type of Lambda
Java 8
import 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());
}
}
λ
Scala
• Have a Function Type used to represent the
Lambda	

• map(f:Integer => Integer)	

• Creates a Function underneath the covers
Scala
object LambdaDemo extends App {
val numbers = List(1, 2, 3)
val numbersPlusOne =
numbers.map(number => number + 1)
}
λ
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));
λ
Clojure
(ns LambdaDemo.core)
(defn -main [& args]
(println(map #(+ % 1) [1, 2, 3])))
λ
JRuby
require "java"
!
array = [1, 2, 3]
array.collect! do |n|
n + 1
end
λ
What is the Problem?
There Are Caveats
Not Reusable
• Lambdas are limited in scope to their call
site	

• You cannot reuse the functionality
elsewhere
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
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
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)	
  
}
} }}}
λλ
λ
λ
λ
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
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
Nashorn Javascript
list.parallelStream().
map(function(e) e / 0)
!
[1, 2, 3]
Infinity,Infinity
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
Scala
val 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
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
JRuby
array.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
Difficult Debugging
• Debuggers on the JVM are getting better -
still hard to debug Lambdas
final List<Integer> numbersPlusOne = numbers.stream().
map(number -> number + 1).collect(Collectors.toList());
NO!
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());
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)
Solution
We want to maintain our ability to program
in a functional style, while having something
maintainable and understandable in
production
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
Named Function
final Function<Integer, Integer> addOneToValue
= number -> number / 0;
!
final List<Integer> numbersPlusOne =
numbers.stream()
.map(addOneToValue)
.collect(Collectors.toList());
Named Function Stack Trace
final Function<Integer, Integer> addOneToValue
= number -> number / 0;
!
final List<Integer> numbersPlusOne =
numbers.stream().map(addOneToValue)
.collect(Collectors.toList());
!
!
Exception in thread "main" java.lang.ArithmeticException: / by zero	
	 at demoFuncs.BadExamples.lambda$main$0(BadExamples.java:15)	
	 at demoFuncs.BadExamples$$Lambda$1/1044036744.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:499)	
	 at demoFuncs.BadExamples.main(BadExamples.java:20)
wat
“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
Stack Trace of a Method	

Java 8
public static Integer addOneToValue(Integer number) {
return number / 0;
}
!
final List<Integer> numbersPlusOne =
numbers.stream()
.map(BadExamples::addOneToValue)
.collect(Collectors.toList());
!
Exception in thread "main" java.lang.ArithmeticException: / by zero	
	 at demoFuncs.BadExamples.addOneToValue(BadExamples.java:12)	
	 at demoFuncs.BadExamples$$Lambda$1/1826771953.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:499)	
	 at demoFuncs.BadExamples.main(BadExamples.java:23)
Much
Better!
Stack Trace of a Method	

Scala
def badFunction = (x: Int) => x / 0
val 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
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
Stack Trace of a Stable Method
def badFunction(x: Int) = x / 0
val 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!
Benefits
• You can’t close over variables	

• Better stack traces	

• More debuggable	

• More testable	

• More maintainable and descriptive	

• Reusable
Rule of Thumb
• Reserve lambda usage for the most basic
expressions	

• Externalize anything more significant than
that to methods
Java 8 Futures
final CompletableFuture<Object> cacheFuture = CompletableFuture
.supplyAsync(() -> {
return cacheRetriever.getCustomer(id);
});
!
cacheFuture.thenApply(customer -> dbService.getOrders(customer))
• java.util.concurrent.CompletableFuture	

• Future operation specified as a Function 	

• Callback specified as Function when the Future
completes
Java 8 vs. Scala
• Scala Functions are a first-class citizens	

• Scala compiler can infer type of Function	

• Java 8 Lambdas mapped to Functional
Interfaces	

• Weaker Type Inference - Can only infer
type based on left hand side of assignment
operator
Java 8 vs. Scala
• Scala still targeting Java 1.6	

• Creates synthetic class to represent Lambda	

• Java 8 Lambdas leverage invoke dynamic	

• Avoids creating synthetic classes	

• http://www.takipiblog.com/2014/01/16/compiling-lambda-
expressions-scala-vs-java-8/
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 - Smart stepping into Lambdas 	

• IntelliJ - Smart Step Into
ThankYou!
• Questions?

What You Need to Know about Lambdas

  • 1.
    @knight_cloud What You Needto Know About Lambdas Ryan Knight Senior Consultant | Typesafe
  • 2.
    Acknowledgement • Material originallycreated by Jamie Allen • @jamie_allen • Modified by Ryan Knight • @knight_cloud
  • 4.
    I Love FunctionalProgramming! • Functional Programming is: • Immutability • Referential Transparency • Functions as first-class citizens • Eliminating side effects
  • 5.
    Why Functional Programming? •Foundation for Reactive Programming • Requires Callbacks to handle Events and Async Results • Avoid Inner Classes • Higher-Level of Abstraction • Define the What not the How
  • 6.
    Imperative Code final 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); }
  • 7.
    We Want DeclarativeCode • Remove temporary lists - List<Integer> numbersPlusOne = Collections.emptyList(); • Remove looping - for (Integer number : numbers) • Focus on what - x+1
  • 8.
    What is aLambda? • A function literal - fixed value in the code. • 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
  • 9.
    Java 8 Lambdas •Functional Interface that defines a Single Abstract Method • Target Type of the Lambda is the Functional Interface • java.util.function - Package with standard Functional Interfaces
  • 10.
    Java 8 FunctionExample @FunctionalInterface public interface Function<T, R> { ! /** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t); Target Type of Lambda
  • 11.
    Java 8 import java.util.List; importjava.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()); } } λ
  • 12.
    Scala • Have aFunction Type used to represent the Lambda • map(f:Integer => Integer) • Creates a Function underneath the covers
  • 13.
    Scala object LambdaDemo extendsApp { val numbers = List(1, 2, 3) val numbersPlusOne = numbers.map(number => number + 1) } λ
  • 14.
    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)); λ
  • 15.
    Clojure (ns LambdaDemo.core) (defn -main[& args] (println(map #(+ % 1) [1, 2, 3]))) λ
  • 16.
    JRuby require "java" ! array =[1, 2, 3] array.collect! do |n| n + 1 end λ
  • 17.
    What is theProblem?
  • 18.
  • 19.
    Not Reusable • Lambdasare limited in scope to their call site • You cannot reuse the functionality elsewhere
  • 20.
    Not Testable inIsolation • 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
  • 21.
    Maintainability • There isnothing 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
  • 22.
    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)   } } }}} λλ λ λ λ
  • 24.
    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
  • 25.
    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
  • 26.
  • 27.
    Favorite Tweet Ever “JavaScriptdoesn't have a dark side, but it does have a dimly lit room full of angry clowns with rubber mallets.” - @odetocode, Jan 5, 2010
  • 28.
    Scala val 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
  • 29.
    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
  • 30.
    JRuby array.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
  • 31.
    Difficult Debugging • Debuggerson the JVM are getting better - still hard to debug Lambdas final List<Integer> numbersPlusOne = numbers.stream(). map(number -> number + 1).collect(Collectors.toList()); NO!
  • 32.
    Digression: Lambdas versusClosures • 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());
  • 33.
    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)
  • 34.
    Solution We want tomaintain our ability to program in a functional style, while having something maintainable and understandable in production
  • 35.
    Named Functions? • Seemslike 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
  • 36.
    Named Function final Function<Integer,Integer> addOneToValue = number -> number / 0; ! final List<Integer> numbersPlusOne = numbers.stream() .map(addOneToValue) .collect(Collectors.toList());
  • 37.
    Named Function StackTrace final Function<Integer, Integer> addOneToValue = number -> number / 0; ! final List<Integer> numbersPlusOne = numbers.stream().map(addOneToValue) .collect(Collectors.toList()); ! ! Exception in thread "main" java.lang.ArithmeticException: / by zero at demoFuncs.BadExamples.lambda$main$0(BadExamples.java:15) at demoFuncs.BadExamples$$Lambda$1/1044036744.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:499) at demoFuncs.BadExamples.main(BadExamples.java:20) wat
  • 38.
    “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
  • 39.
    Stack Trace ofa Method Java 8 public static Integer addOneToValue(Integer number) { return number / 0; } ! final List<Integer> numbersPlusOne = numbers.stream() .map(BadExamples::addOneToValue) .collect(Collectors.toList()); ! Exception in thread "main" java.lang.ArithmeticException: / by zero at demoFuncs.BadExamples.addOneToValue(BadExamples.java:12) at demoFuncs.BadExamples$$Lambda$1/1826771953.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:499) at demoFuncs.BadExamples.main(BadExamples.java:23) Much Better!
  • 40.
    Stack Trace ofa Method Scala def badFunction = (x: Int) => x / 0 val 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
  • 41.
    Digression • You candefine 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
  • 42.
    Stack Trace ofa Stable Method def badFunction(x: Int) = x / 0 val 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!
  • 43.
    Benefits • You can’tclose over variables • Better stack traces • More debuggable • More testable • More maintainable and descriptive • Reusable
  • 44.
    Rule of Thumb •Reserve lambda usage for the most basic expressions • Externalize anything more significant than that to methods
  • 45.
    Java 8 Futures finalCompletableFuture<Object> cacheFuture = CompletableFuture .supplyAsync(() -> { return cacheRetriever.getCustomer(id); }); ! cacheFuture.thenApply(customer -> dbService.getOrders(customer)) • java.util.concurrent.CompletableFuture • Future operation specified as a Function • Callback specified as Function when the Future completes
  • 46.
    Java 8 vs.Scala • Scala Functions are a first-class citizens • Scala compiler can infer type of Function • Java 8 Lambdas mapped to Functional Interfaces • Weaker Type Inference - Can only infer type based on left hand side of assignment operator
  • 47.
    Java 8 vs.Scala • Scala still targeting Java 1.6 • Creates synthetic class to represent Lambda • Java 8 Lambdas leverage invoke dynamic • Avoids creating synthetic classes • http://www.takipiblog.com/2014/01/16/compiling-lambda- expressions-scala-vs-java-8/
  • 48.
    Language Creators • Languagedesigners and tool producers need to help us • At Typesafe, we’re making our Eclipse-based Scala IDE more lambda-friendly with each release - Smart stepping into Lambdas • IntelliJ - Smart Step Into
  • 49.