WhatYou Need ToKnow About
@jamie_allenJamie	  Allen•Director	  of	  Consulting	  •User	  of	  Lambdas	  on	  the	  JVM	  for	  4+	  years
@jamie_allenI	  Love	  Functional	  Programming!•Functional	  Programming	  is:•Immutable	  state•Referential	  transparen...
@jamie_allenWhat	  is	  a	  Lambda?•A	  Function	  Literal•A	  function	  that	  is	  not	  bound	  to	  a	  name,	  to	  ...
@jamie_allenJava	  8import java.util.List;import java.util.Arrays;import java.util.stream.Collectors;public class LambdaDe...
@jamie_allenScalaobject LambdaDemo extends App {val numbers = List(1, 2, 3)val numbersPlusOne =numbers.map(number => numbe...
@jamie_allenClojure(ns LambdaDemo.core)(defn -main [& args](println(map #(+ % 1) [1, 2, 3])))λ
@jamie_allenJRubyrequire "java"array = [1, 2, 3]array.collect! do |n|n + 1endλ
@jamie_allenSo	  What	  Is	  The	  Problem?
@jamie_allenNot	  Reusable•Lambdas	  are	  limited	  in	  scope	  to	  their	  call	  site•You	  cannot	  reuse	  the	  fu...
@jamie_allenNot	  Testable	  in	  Isolation•How	  do	  you	  test	  code	  that	  does	  not	  have	  a	  name	  through	 ...
@jamie_allenMaintainability•Developers	  have	  to	  read	  through	  the	  lambda	  to	  figure	  out	  what	  it’s	  tryi...
@jamie_allenLousy	  Stack	  Traces•Compilers	  have	  to	  come	  up	  with	  generic	  names	  for	  the	  classes	  that...
@jamie_allenJava	  8final List<Integer> numbersPlusOne =numbers.stream().map(number -> number / 0).collect(Collectors.toLi...
@jamie_allenScalaval numbersPlusOne = numbers.map(number => number / 0)Exception in thread "main" java.lang.ArithmeticExce...
@jamie_allenClojureprintln(map #(/ % 0) [1, 2, 3])))Exception in thread "main" (java.lang.ArithmeticException: Divide by z...
@jamie_allenJRubyarray.collect! do |n|n / 0ZeroDivisionError: divided by 0/ at org/jruby/RubyFixnum.java:547(root) at Hell...
@jamie_allenDifficult	  Debugging•Debuggers	  on	  the	  JVM	  can	  only	  disambiguate	  code	  at	  the	  source	  line,	...
@jamie_allenDigression:	  Lambdas	  vs	  Closures•Closures	  are	  merely	  lambdas	  that	  “close	  over”	  state	  avai...
@jamie_allenClosing	  Over	  State•Lambdas	  have	  access	  to	  all	  state	  within	  their	  enclosing	  scope•It	  is...
@jamie_allenSOLUTION•We	  want	  to	  maintain	  our	  ability	  to	  program	  in	  a	  functional	  style,	  while	  hav...
@jamie_allenNamed	  Functions?•Seems	  like	  it	  would	  help,	  but	  it	  depends	  on	  the	  compiler	  and	  how	  ...
@jamie_allenNamed	  Functionobject LambdaTest extends App {val addOneToValue = (x: Int) => x + 1val myList = (1 to 20).map...
@jamie_allenStack	  Trace?val badFunction = (x: Int) => x / 0val myList = (1 to 20).map(badFunction(_))Exception in thread...
@jamie_allenEta	  Expansion•“Lifting”,	  or	  coercing,	  a	  method	  to	  be	  used	  as	  a	  function•Must	  meet	  th...
@jamie_allenMethods	  as	  Functions•In	  Scala,	  if	  we	  use	  a	  method,	  it	  can	  be	  “lifted”	  into	  a	  fun...
@jamie_allenStack	  Trace	  of	  Methoddef badFunction = (x: Int) => x / 0val myList = (1 to 20).map(badFunction(_))Except...
@jamie_allenDigression•You	  can	  define	  your	  function	  like	  this,	  but	  “def”	  is	  not	  stable	  -­‐	  it	  w...
@jamie_allenStack	  Trace	  of	  Methoddef badFunction(x: Int) = x / 0val myList = (1 to 20).map(badFunction(_))Exception ...
@jamie_allenBenefits•Can’t	  close	  over	  variables•Internal	  variables	  are	  operands•Better	  stack	  traces•More	  ...
@jamie_allenRule	  of	  Thumb•Keep	  lambda	  usage	  for	  the	  smallest	  expressions•Externalize	  anything	  more	  t...
@jamie_allenLanguage	  Creators•Language	  designers	  need	  to	  help	  you	  with	  this!•At	  Typesafe,	  we’re	  maki...
@jamie_allenThank	  You!•Questions?
Upcoming SlideShare
Loading in …5
×

What you need to know about Lambdas - Jamie Allen

848 views

Published on

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 may not realize the costs and pitfalls associated with this style of programming. In this talk, we will discuss the many issues of using Lambdas in Java as well as Scala, and measures we can talk to avoid them. We will also review concepts associated with lambdas to make sure everyone is on the same page, such as closures, higher order functions and Eta Expansion.

Published in: Technology, News & Politics
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
848
On SlideShare
0
From Embeds
0
Number of Embeds
20
Actions
Shares
0
Downloads
28
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

What you need to know about Lambdas - Jamie Allen

  1. 1. WhatYou Need ToKnow About
  2. 2. @jamie_allenJamie  Allen•Director  of  Consulting  •User  of  Lambdas  on  the  JVM  for  4+  years
  3. 3. @jamie_allenI  Love  Functional  Programming!•Functional  Programming  is:•Immutable  state•Referential  transparency•Functions  as  first-­‐class  citizens
  4. 4. @jamie_allenWhat  is  a  Lambda?•A  Function  Literal•A  function  that  is  not  bound  to  a  name,  to  be  used  only  within  the  context  of  where  it  is  defined•Merely  an  implementation  detail  of  Functional  Programming!
  5. 5. @jamie_allenJava  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());}}λ
  6. 6. @jamie_allenScalaobject LambdaDemo extends App {val numbers = List(1, 2, 3)val numbersPlusOne =numbers.map(number => number + 1)}λ
  7. 7. @jamie_allenClojure(ns LambdaDemo.core)(defn -main [& args](println(map #(+ % 1) [1, 2, 3])))λ
  8. 8. @jamie_allenJRubyrequire "java"array = [1, 2, 3]array.collect! do |n|n + 1endλ
  9. 9. @jamie_allenSo  What  Is  The  Problem?
  10. 10. @jamie_allenNot  Reusable•Lambdas  are  limited  in  scope  to  their  call  site•You  cannot  reuse  the  functionality  elsewhere
  11. 11. @jamie_allenNot  Testable  in  Isolation•How  do  you  test  code  that  does  not  have  a  name  through  which  you  can  call  it?•You  can  only  test  lambdas  in  the  context  of  their  call  site
  12. 12. @jamie_allenMaintainability•Developers  have  to  read  through  the  lambda  to  figure  out  what  it’s  trying  to  do•The  more  complex  the  lambda,  the  harder  this  is  to  do•Waste  of  valuable  development  time
  13. 13. @jamie_allenLousy  Stack  Traces•Compilers  have  to  come  up  with  generic  names  for  the  classes  that  represent  the  lambdas  on  the  JVM,  called  “name  mangling”•The  stack  trace  output  tells  you  very  little  about  where  the  problem  occurred
  14. 14. @jamie_allenJava  8final List<Integer> numbersPlusOne =numbers.stream().map(number -> number / 0).collect(Collectors.toList());Exception in thread "main" java.lang.ArithmeticException: / by zeroat LambdaDemo.lambda$0(LambdaDemo.java:1)at LambdaDemo$$Lambda$1.apply(Unknown Source)at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:188)at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:467)at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:457)at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:710)at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:231)at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:474)at LambdaDemo.main(LambdaDemo.java:9)wat
  15. 15. @jamie_allenScalaval numbersPlusOne = numbers.map(number => number / 0)Exception in thread "main" java.lang.ArithmeticException: / by zeroat 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
  16. 16. @jamie_allenClojureprintln(map #(/ % 0) [1, 2, 3])))Exception in thread "main" (java.lang.ArithmeticException: Divide by zeroat 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
  17. 17. @jamie_allenJRubyarray.collect! do |n|n / 0ZeroDivisionError: divided by 0/ at org/jruby/RubyFixnum.java:547(root) at HelloWorld.rb:11collect! at org/jruby/RubyArray.java:2385(root) at HelloWorld.rb:10not half bad, really
  18. 18. @jamie_allenDifficult  Debugging•Debuggers  on  the  JVM  can  only  disambiguate  code  at  the  source  line,  so  you  need  to  make  your  lambdas  multi-­‐line  to  be  able  to  step  through  them•Some  languages  allow  lambdas  to  use  “placeholders”  instead  of  names,  which  cannot  currently  be  “watched”  in  a  debugger
  19. 19. @jamie_allenDigression:  Lambdas  vs  Closures•Closures  are  merely  lambdas  that  “close  over”  state  available  to  them  from  their  enclosing  scopeval x = 1(1 to 20).map(num => num + x)
  20. 20. @jamie_allenClosing  Over  State•Lambdas  have  access  to  all  state  within  their  enclosing  scope•It  is  very  easy  to  manipulate  these  values  and  change  something  important•If  the  lambda  is  in  use  for  a  Future  operation,  this  can  lead  to  race  conditions
  21. 21. @jamie_allenSOLUTION•We  want  to  maintain  our  ability  to  program  in  a  functional  style,  while  having  something  usable  in  production
  22. 22. @jamie_allenNamed  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  the  stack  trace  will  still  not  show  you  the  name  of  the  function  you  used
  23. 23. @jamie_allenNamed  Functionobject LambdaTest extends App {val addOneToValue = (x: Int) => x + 1val myList = (1 to 20).map(addOneToValue(_))println(myList)}
  24. 24. @jamie_allenStack  Trace?val 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
  25. 25. @jamie_allenEta  Expansion•“Lifting”,  or  coercing,  a  method  to  be  used  as  a  function•Must  meet  the  contract  of  the  lambda  usage  defined  by  the  compiler  in  use,  such  as  one  argument  for  the  value  to  be  manipulated
  26. 26. @jamie_allenMethods  as  Functions•In  Scala,  if  we  use  a  method,  it  can  be  “lifted”  into  a  function  and  give  us  everything  we  need•But  what  syntax  should  we  use?
  27. 27. @jamie_allenStack  Trace  of  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)Yay!
  28. 28. @jamie_allenDigression•You  can  define  your  function  like  this,  but  “def”  is  not  stable  -­‐  it  will  re-­‐evaluate  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  syntaxdef badFunction(x: Int) = x / 0
  29. 29. @jamie_allenStack  Trace  of  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)Even better!
  30. 30. @jamie_allenBenefits•Can’t  close  over  variables•Internal  variables  are  operands•Better  stack  traces•More  debuggable•More  testable•Easier  maintenance•Reusability
  31. 31. @jamie_allenRule  of  Thumb•Keep  lambda  usage  for  the  smallest  expressions•Externalize  anything  more  than  the  most  basic  logic  into  methods
  32. 32. @jamie_allenLanguage  Creators•Language  designers  need  to  help  you  with  this!•At  Typesafe,  we’re  making  our  Scala  IDE  debugger  more  lambda-­‐friendly  with  each  version
  33. 33. @jamie_allenThank  You!•Questions?

×