• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
What You Need to Know About Lambdas - Jamie Allen (Typesafe)
 

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

on

  • 765 views

Presented as a keynote at JAX London 2013 ...

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.

Statistics

Views

Total Views
765
Views on SlideShare
764
Embed Views
1

Actions

Likes
0
Downloads
19
Comments
0

1 Embed 1

https://twitter.com 1

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

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

    • Jamie Allen | Typesafe What You Need to Know About Lambdas Wednesday, November 6, 13
    • Who Am I? • Director of Consulting for • User of lambdas on the JVM for ~5 years @jamie_allen Wednesday, November 6, 13
    • Wednesday, November 6, 13
    • Wednesday, November 6, 13
    • Wednesday, November 6, 13
    • I Love Functional Programming! • Functional Programming is: • Immutability • Referential Transparency • Functions as first-class citizens Wednesday, November 6, 13
    • We Want Declarative 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); } Wednesday, November 6, 13
    • 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
    • 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); } } Wednesday, November 6, 13 λ final List<Integer> numbersPlusOne = numbers.stream().map(number -> number + 1). collect(Collectors.toList());
    • 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 λ
    • Scala λ object LambdaDemo extends App { val numbers = List(1, 2, 3) val numbersPlusOne = numbers.map(number => number + 1) } Wednesday, November 6, 13
    • Clojure λ (ns LambdaDemo.core) (defn -main [& args] (println(map #(+ % 1) [1, 2, 3]))) Wednesday, November 6, 13
    • JRuby require "java" array = [1, 2, 3] array.collect! do |n| n + 1 end Wednesday, November 6, 13 λ
    • What is the Problem? Wednesday, November 6, 13
    • There Are Caveats Wednesday, November 6, 13
    • Not Reusable • Lambdas are limited in scope to their call site • You cannot reuse the functionality elsewhere Wednesday, November 6, 13
    • 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
    • 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
    • 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 }} }λ }λ } λ λ λ
    • Wednesday, November 6, 13
    • 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
    • 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
    • Nashorn Javascript list.parallelStream(). map(function(e) e / 0) [1, 2, 3] Infinity,Infinity Wednesday, November 6, 13
    • Nashorn Javascript list.parallelStream(). map(function(e) e / 0) [1, 2, 3] Infinity,Infinity Wednesday, November 6, 13
    • 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
    • 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 Wednesday, November 6, 13
    • Clojure println(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
    • 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 Wednesday, November 6, 13
    • 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
    • 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
    • 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
    • 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
    • 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
    • Named Function object LambdaTest extends App { val addOneToValue = (x: Int) => x + 1 } val myList = (1 to 20).map(addOneToValue) Wednesday, November 6, 13
    • Named Function Stack Trace val 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$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
    • “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
    • Stack Trace of a 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$$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
    • 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 • Better to stick with simple method syntax instead def badFunction(x: Int) = x / 0 Wednesday, November 6, 13
    • 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! Wednesday, November 6, 13
    • Benefits • You can’t close over variables • Better stack traces • More debuggable • More testable • More maintainable and descriptive • Reusable Wednesday, November 6, 13
    • Rule of Thumb • Reserve lambda usage for the most basic expressions • Externalize anything more significant than that to methods Wednesday, November 6, 13
    • 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
    • Thank You! • Questions? Wednesday, November 6, 13