Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RxJava: Reactive Extensions in Scala

RxJava: Reactive Extensions in Scala

RxJava is a library for composing asynchronous and event-based programs by using observable sequences for the Java VM. In this presentation, I discuss some interesting consequences of building a library intended to be consumed by multiple JVM languages. I also work through some examples of using this abstraction in Scala.

Presented at SF-Scala on October 17, 2013. http://www.meetup.com/SF-Scala/events/142643212.

RxJava at Netflix: http://techblog.netflix.com/2013/02/rxjava-netflix-api.html

RxJava on Github: https://github.com/Netflix/RxJava

73f44a0e40795d368079870b69ab7ef1?s=128

Matt Jacobs

October 18, 2013
Tweet

Transcript

  1. RxJava:  Reactive   Extensions  in  Scala   Ma#  Jacobs  /

     @ma#rjacobs   h#ps://github.com/ma#rjacobs/RxScalaDemo   h#p://techblog.ne>lix.com    
  2. Agenda   •  For  library  writers   How  does  RxJava

     support  Scala  in  parEcular?   How  does  RxJava  support  many  JVM  languages?   •  For  RxJava  consumers  (or  prospecEve  consumers)  
  3. Agenda   •  For  library  writers   •  How  does

     RxJava  support  Scala  in  parEcular?   How  does  RxJava  support  many  JVM  languages?   •  For  RxJava  consumers  (or  prospecEve  consumers)  
  4. Agenda   •  For  library  writers   •  How  does

     RxJava  support  Scala  in  parEcular?   •  How  does  RxJava  support  many  JVM  languages?   •  For  RxJava  consumers  (or  prospecEve  consumers)  
  5. Agenda   •  For  library  writers   •  How  does

     RxJava  support  Scala  in  parEcular?   •  How  does  RxJava  support  many  JVM  languages?   •  For  RxJava  consumers  (or  prospecEve  consumers)   •  How  do  other  concurrency/collecEon  constructs  map  onto   Observable?  
  6. Agenda   •  For  library  writers   •  How  does

     RxJava  support  Scala  in  parEcular?   •  How  does  RxJava  support  many  JVM  languages?   •  For  RxJava  consumers  (or  prospecEve  consumers)   •  How  do  other  concurrency/collecEon  constructs  map  onto   Observable?   •  What  can  I  do  with  it?  
  7. RxJava  Design  Goals   •  Adherence  to  .NET  spec  

    Type  safety   Support  all  JVM  languages  in  a  language-­‐naEve  way   Consumers  can  pick  which  languages  they  want  to  depend  on   Consumers  can  mix  mulEple  JVM  languages  in  the  same     project  
  8. RxJava  Design  Goals   •  Adherence  to  .NET  spec  

    •  Type  safety   Support  all  JVM  languages  in  a  language-­‐naEve  way   Consumers  can  pick  which  languages  they  want  to  depend  on   Consumers  can  mix  mulEple  JVM  languages  in  the  same     project  
  9. RxJava  Design  Goals   •  Adherence  to  .NET  spec  

    •  Type  safety   •  Support  all  JVM  languages  in  a  language-­‐naEve  way   Consumers  can  pick  which  languages  they  want  to  depend  on   Consumers  can  mix  mulEple  JVM  languages  in  the  same     project  
  10. RxJava  Design  Goals   •  Adherence  to  .NET  spec  

    •  Type  safety   •  Support  all  JVM  languages  in  a  language-­‐naEve  way   •  Consumers  can  pick  which  languages  they  want  to  depend  on   Consumers  can  mix  mulEple  JVM  languages  in  the  same     project  
  11. RxJava  Design  Goals   •  Adherence  to  .NET  spec  

    •  Type  safety   •  Support  all  JVM  languages  in  a  language-­‐naEve  way   •  Consumers  can  pick  which  languages  they  want  to  depend  on   •  Consumers  can  mix  mulEple  languages  in  the  same  project  
  12. Java  Observable   public  class  Observable<T>  {    public  <R>

     Observable<R>  map  (            Func1<?  super  T,  ?  extends  R>  f);   }     public  interface  Func1<T1,  R>  {          public  R  call(T1  t1);   }     Observable<Integer>  o  =  Observable.just(1);     o.map(new  Func1<Integer,  String>()  {    @Override    public  String  call(Integer  i)  {                  return  (i  +  3).toString();            }   });  
  13. Java  Observable   public  class  Observable<T>  {    public  <R>

     Observable<R>  map  (            Func1<?  super  T,  ?  extends  R>  f);   }     public  interface  Func1<T1,  R>  {          public  R  call(T1  t1);   }     Observable<Integer>  o  =  Observable.just(1);     o.map(new  Func1<Integer,  String>()  {    @Override    public  String  call(Integer  i)  {                  return  (i  +  3).toString();            }   });  
  14. Java  Observable   Observable<Integer>  o  =  Observable.just(1);     o.map(new

     Func1<Integer,  String>()  {    @Override    public  String  call(Integer  i)  {                  return  (i  +  3).toString();            }   });  
  15. Using  Observable  in  Java   Observable<Integer>  o  =  Observable.just(1);  

      o.map(new  Func1<Integer,  String>()  {    @Override    public  String  call(Integer  i)  {                  return  (i  +  3).toString();           Observable<Integer>  o  =  Observable.just(1);     o.map(new  Func1<Integer,  String>()  {    @Override    public  String  call(Integer  i)  {                  return  (i  +  3).toString();            }   });;  
  16. Using  Observable  in  Java   Observable<Integer>  o  =  Observable.just(1);  

      o.map(new  Func1<Integer,  String>()  {    @Override    public  String  call(Integer  i)  {                  return  (i  +  3).toString();           Observable<Integer>  o  =  Observable.just(1);     o.map(new  Func1<Integer,  String>()  {    @Override    public  String  call(Integer  i)  {                  return  (i  +  3).toString();            }   });;  
  17. Using  Observable  in  Scala   Observable<Integer>  o  =  Observable.just(1);  

      o.map(new  Func1<Integer,  String>()  {    @Override    public  String  call(Integer  i)  {                  return  (i  +  3).toString();       val  o:  Observable[Int]  =  Observable.just(1)       o.map(i  =>  (i  +  3).toString)    
  18. Using  Observable  in  Scala   Observable<Integer>  o  =  Observable.just(1);  

      o.map(new  Func1<Integer,  String>()  {    @Override    public  String  call(Integer  i)  {                  return  (i  +  3).toString();       val  o:  Observable[Int]  =  Observable.just(1)       o.map(i  =>  (i  +  3).toString)    
  19. Using  Observable  in  Groovy   Observable<Integer>  o  =  Observable.just(1);  

      o.map(new  Func1<Integer,  String>()  {    @Override    public  String  call(Integer  i)  {                  return  (i  +  3).toString();       Observable<Integer>  o  =  Observable.just(1)       o.map  {  i  -­‐>  (i  +  3).toString  }  
  20. Using  Observable  in  Groovy   Observable<Integer>  o  =  Observable.just(1);  

      o.map(new  Func1<Integer,  String>()  {    @Override    public  String  call(Integer  i)  {                  return  (i  +  3).toString();       Observable<Integer>  o  =  Observable.just(1)       o.map  {  i  -­‐>  (i  +  3).toString  }  
  21. Multiple  Language  Support   •  Java        

     public  <R>  Observable<R>  map(Func1<T,  R>  f);   Scala            def  map[R](f:  T  =>  R)   Groovy          def  map(groovy.lang.Closure)  
  22. Multiple  Language  Support   •  Java        

     public  <R>  Observable<R>  map(Func1<T,  R>  f);   •  Scala            def  map[R](f:  T  =>  R):  Observable[R]   Groovy          def  map(groovy.lang.Closure)  
  23. Multiple  Language  Support   •  Java        

     public  <R>  Observable<R>  map(Func1<T,  R>  f);   •  Scala            def  map[R](f:  T  =>  R):  Observable[R]   •  Groovy          def  Observable<R>  map(groovy.lang.Closure<R>  f)  
  24. Multiple  Language  Support   rx/Observable.class   map(Func1<T,  R>  f);  

      map(f:  T  =>  R)   map(Closure<R>  f)  
  25. Scala  support   •  In  order  to  support  Scala  naEvely,

     we  need:   •  Good  interop  between  Scala  funcEons  and  RxJava  core    
  26. Scala  support   •  In  order  to  support  Scala  naEvely,

     we  need:   •  Good  interop  between  Scala  funcEons  and  RxJava  core   •  Ability  to  add  idiomaEc  Scala  methods  to  rx.Observable   •  apply()  factory  methods   •  ++   •  drop(n:  Int)  instead  of  skip(n:  Int)     •  zip  always  returns  a  tuple  
  27. Implicit  conversions   •  We  have:        

       public  <R>  Observable<R>  map(Func1<T,  R>  f);   •   We  want:                      def  map[R](f:  T  =>  R):  Observable[R]     Implicit  conversion:          implicit  def  toRxFunc1[A,  B](f:  (A  =>  B)):  Func1[A,  B]  =          new  Func1[A,  B]  {              def  call(a:  A):  B  =  f(a)          }     We  added  18  such  conversions  from  Scala  funcEons  to  Rx  funcEons   Lots  of  duplicaEve  code  –  could  macros  help?      
  28. Implicit  conversions   •  We  have:        

       public  <R>  Observable<R>  map(Func1<T,  R>  f);   •   We  want:                      def  map[R](f:  T  =>  R):  Observable[R]     •  Implicit  conversion:          implicit  def  toRxFunc1[A,  B](f:  (A  =>  B)):  Func1[A,  B]  =              new  Func1[A,  B]  {                  def  call(a:  A):  B  =  f(a)              }     We  added  18  such  conversions  from  Scala  funcEons  to  Rx  funcEons   Lots  of  duplicaEve  code  –  could  macros  help?      
  29. Implicit  conversions   •  We  have:        

       public  <R>  Observable<R>  map(Func1<T,  R>  f);   •   We  want:                      def  map[R](f:  T  =>  R):  Observable[R]     •  Implicit  conversion:          implicit  def  toRxFunc1[A,  B](f:  (A  =>  B)):  Func1[A,  B]  =              new  Func1[A,  B]  {                  def  call(a:  A):  B  =  f(a)              }   •  We  added  18  such  conversions  from  Scala  funcEons  to  Rx  funcEons   •  Lots  of  duplicaEve  code  for  ariEes–  could  macros  help?      
  30. Scala  support   •  In  order  to  support  Scala  naEvely,

     we  need:   •  Good  interop  between  Scala  funcEons  and  RxJava  core   •  Ability  to  add  idiomaEc  Scala  methods  to  rx.Observable   •  apply()  factory  methods   •  ++   •  drop(n)  instead  of  skip(n)     •  zip  always  returns  a  tuple  
  31. Extension  methods   •  Implicit  conversion  to  rx.lang.scala.Observable    

       
  32. Extension  methods   •  Implicit  conversion  to  rx.lang.scala.Observable    

        implicit  def  toScalaObs[T](asJava:  rx.Observable[T])  =        new  rx.lang.scala.Observable(asJava)  
  33. Extension  methods   •  Implicit  conversion  to  rx.lang.scala.Observable    

        implicit  def  toScalaObs[T](asJava:  rx.Observable[T])  =        new  rx.lang.scala.Observable(asJava)     •  Define  new  methods  on  rx.lang.scala.Observable      class  Observable[+T](asJava:  rx.Observable[T])  {          def  ++[U  >:  T](that:  Observable[U]):  Observable[U]  =  ...          def  drop(int:  N):  Observable[T]  =  ...          ...      }  
  34. Extension  methods   •  Implicit  conversion  to  rx.lang.scala.Observable   • 

    Define  all  new  methods  on  rx.lang.scala.Observable     •  Works,  but…   •  Other  languages  can’t  consume  rx.lang.scala.Observable  
  35. Extension  methods   •  Implicit  conversion  to  rx.lang.scala.Observable   • 

    Define  all  new  methods  on  rx.lang.scala.Observable     •  Works,  but…   •  Other  languages  can’t  consume  rx.lang.scala.Observable   •  We  incur  the  cost  of  wrapping  very  frequently  
  36. Scala  value  classes  (SIP-­‐15)   •  Thanks  to  @samuelgrue#er,  @jmhofer

           package  rx.lang.scala        class  Observable[+T]  (val  asJava:  rx.Observable[_  <:  T])        extends  AnyVal  
  37. Scala  value  classes  (SIP-­‐15)   •  Thanks  to  @samuelgrue#er  

         package  rx.lang.scala        class  Observable[+T]  (val  asJava:  rx.Observable[_  <:  T])        extends  AnyVal       •  Does  not  incur  wrapping  costs  
  38. Scala  value  classes  (SIP-­‐15)   •  Thanks  to  @samuelgrue#er  

         package  rx.lang.scala        class  Observable[+T]  (val  asJava:  rx.Observable[_  <:  T])        extends  AnyVal       •  Does  not  incur  wrapping  costs   •  Works  transparently  with  any  other  JVM  language  
  39. Scala  value  class  example   object  Producer  {    

     def  stream():  rx.lang.scala.Observable[String]  =  {          val  stringList  =  (1  to  100).map(_  =>  "a")          Observable(stringList:  _*)      }   }     public  class  SampleConsumer  {          public  static  void  main(String[]  args)  {            Observable<?  extends  String>  s  =  Producer.stream();                    s.subscribe(new  Action1<String>()  {                          @Override                          public  void  call(String  o)  {                                  System.out.println("onNext  :  "  +  o);                          }});}}  
  40. Scala  value  class  example   object  Producer  {    

     def  stream():  rx.lang.scala.Observable[String]  =  {          val  stringList  =  (1  to  100).map(_  =>  "a")          Observable(stringList:  _*)      }   }     public  class  SampleConsumer  {          public  static  void  main(String[]  args)  {                rx.Observable<?  extends  String>  s  =  Producer.stream();                  s.subscribe(new  Action1<String>()  {                        @Override                        public  void  call(String  o)  {                                System.out.println("onNext  :  "  +  o);                        }});                  }            }  
  41. Multiple  Language  Support   rx/Observable.class   map(Func1<T,  R>  f);  

      map(f:  T  =>  R)   map(Closure<R>  f)  
  42. Implementation  Strategies   •  All  JVM  languages  supported  by  rx.Observable

     via  source      public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);          public  <R>  Observable<R>  map(groovy.lang.Closure<R>  f);          public  <R>  Observable<R>  map(clojure.lang.IFn  f);            }    
  43. Implementation  Strategies   •  All  JVM  languages  supported  by  rx.Observable

     via  source      public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);          public  <R>  Observable<R>  map(groovy.lang.Closure<R>  f);          public  <R>  Observable<R>  map(clojure.lang.IFn  f);            }     •  Would  force  consumers  of  RxJava  to  pull  in  libraries  for:   •  Clojure,  Groovy,  JRuby,  Kotlin,  …  
  44. Implementation  Strategies   •  All  JVM  languages  supported  by  rx.Observable

     via  source      public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);          public  <R>  Observable<R>  map(groovy.lang.Closure<R>  f);          public  <R>  Observable<R>  map(clojure.lang.IFn  f););            }     •  Would  force  consumers  of  RxJava  to  pull  in  libraries  for:   •  Clojure,  Groovy,  JRuby,  Kotlin,  …  
  45. Implementation  Strategies   •  Method  overloads  with  Object  arguments  

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);          public  <R>  Observable<R>  map(Object  f);      }    
  46. Implementation  Strategies   •  Method  overloads  with  Object  arguments  

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);          public  <R>  Observable<R>  map(Object  f)  {              Func1<T,  R>  typedFunc  =  LanguageAdaptor.getFunc(f);              this.map(typedFunc);          }      }    
  47. Implementation  Strategies   •  Method  overloads  with  Object  arguments  

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);          public  <R>  Observable<R>  map(Object  f)  {              Func1<T,  R>  typedFunc  =  LanguageAdaptor.getFunc(f);              this.map(typedFunc);          }      }    
  48. Implementation  Strategies   •  Method  overloads  with  Object  arguments  

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);          public  <R>  Observable<R>  map(Object  f)  {              Func1<T,  R>  typedFunc  =  LanguageAdaptor.getFunc(f);              this.map(typedFunc);          }      }        public  class  GroovyAdaptor  {          public  <T,  R>  Func1<T,  R>  getFunc(Object  o)  {              if  (o.getClass().equals(groovy.lang.Closure.class)                  //convert  Closure  to  Func1  and  return          }  }    
  49. Implementation  Strategies   •  Method  overloads  with  Object  arguments  

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);          public  <R>  Observable<R>  map(Object  f);      }   •  This  works!  –  RxJava  up  to  0.11  used  this  approach    
  50. Implementation  Strategies   •  Method  overloads  with  Object  arguments  

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);          public  <R>  Observable<R>  map(Object  f);      }   •  This  works!  –  RxJava  up  to  0.11  used  this  approach   •  However,  not  typesafe     val  o:  Observable[Int]  =  Observable.just(1)     o.map((l:  List[String])  =>  l.head)      
  51. Implementation  Strategies   •  Method  overloads  with  Object  arguments  

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);          public  <R>  Observable<R>  map(Object  f);      }   •  This  works!  –  RxJava  up  to  0.11  used  this  approach   •  However,  not  typesafe     val  o:  Observable[Int]  =  Observable.just(1)     o.map((l:  List[String])  =>  l.head)      
  52. Implementation  Strategies   •  Method  overloads  with  Object  arguments  

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);          public  <R>  Observable<R>  map(Object  f);      }   •  This  works!  –  RxJava  up  to  0.11  used  this  approach   •  However,  not  typesafe     val  o:  Observable[Int]  =  Observable.just(1)     o.map((l:  List[String])  =>  l.head)     [success]  Total  time:  0s    
  53. Implementation  Strategies   •  Method  overloads  with  Object  arguments  

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);          public  <R>  Observable<R>  map(Object  f);      }   •  This  works!  –  RxJava  up  to  0.11  used  this  approach   •  However,  not  typesafe     val  o:  Observable[Int]  =  Observable.just(1)     o.map((l:  List[String])  =>  l.head)     [success]  Total  time:  0s    
  54. Implementation  Strategies   •  StaEc  bytecode  rewriEng  

  55. Implementation  Strategies   •  StaEc  bytecode  rewriEng      

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);          public  <R>  Observable<R>  map(Object  f);      }  
  56. Implementation  Strategies   •  StaEc  bytecode  rewriEng      

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);          public  <R>  Observable<R>  map(Object  f);      }  
  57. Implementation  Strategies   •  StaEc  bytecode  rewriEng      

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);      }           rx/Observable.class   Observable<R>   map(Func1<T,  R>  f)   rx/Observable.class   Observable<R>   map(Closure<R>  f)   rx/Observable.class   Observable<R>   map(Func1<T,  R>  f)   Observable<R>   map(Closure<R>  f)   Package  (1)  
  58. Implementation  Strategies   •  StaEc  bytecode  rewriEng      

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);      }           rx/Observable.class   Observable<R>   map(Func1<T,  R>  f)   rx/Observable.class   Observable<R>   map(Closure<R>  f)   rx/Observable.class   Observable<R>   map(Func1<T,  R>  f)   Observable<R>   map(Closure<R>  f)   Package  (1)   Code-­‐gen  
  59. Implementation  Strategies   •  StaEc  bytecode  rewriEng      

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);      }           rx/Observable.class   Observable<R>   map(Func1<T,  R>  f)   rx/Observable.class   Observable<R>   map(Closure<R>  f)   rx/Observable.class   Observable<R>   map(Func1<T,  R>  f)   Observable<R>   map(Closure<R>  f)   Package  (1)   Code-­‐gen   Package  (2)  
  60. Implementation  Strategies   •  StaEc  bytecode  rewriEng      

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);      }           rx/Observable.class     Observable<R>  map(Func1<T,  R>  f)   Observable<R>  map(Closure<R>  f)    
  61. Implementation  Strategies   •  StaEc  bytecode  rewriEng      

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);      }           •  Causes  havoc  on  tools  unless  we  depend  on  all  languages   (scalac,  IDEs,  …)   rx/Observable.class     Observable<R>  map(Func1<T,  R>  f)   Observable<R>  map(Closure<R>  f)    
  62. Implementation  Strategies   •  StaEc  bytecode  rewriEng      

         public  class  Observable<T>  {          public  <R>  Observable<R>  map(Func1<T,  R>  f);      }           •  Causes  havoc  on  tools  unless  we  depend  on  all  languages   (scalac,  IDEs,  …)   Observable.class     Observable<R>  map(Func1<T,  R>  f)   Observable<R>  map(Closure<R>  f)    
  63. Implementation  Strategies   •  Dynamic  bytecode  rewriEng    

  64. Implementation  Strategies   •  Dynamic  bytecode  rewriEng   •  If

     there  is  1  factory  method  for  gekng  rx.Observables:   •  Can  rewrite  that  to  provide  an  rx.DynamicObservable  (which  only   exists  at  runEme  and  extends  rx.Observable)   •  rx.DynamicObservable  has  the  Object-­‐overloaded  method      public  <R>  DynamicObservable<R>  map(Object  f);      with  an  implementaEon  which  properly  delegates  to            language-­‐specific  adaptors      
  65. Implementation  Strategies   •  Dynamic  bytecode  rewriEng   •  If

     there  is  1  factory  method  for  gekng  rx.Observables:   •  Can  rewrite  that  to  provide  an  rx.DynamicObservable  (which  only   exists  at  runEme  and  extends  rx.Observable)   •  rx.DynamicObservable  has  the  Object-­‐overloaded  method      public  <R>  DynamicObservable<R>  map(Object  f);      with  an  implementaEon  which  properly  delegates  to            language-­‐specific  adaptors   •  Needless  to  say,  this  is  really  complex  and  hard  to  debug   •  Also,  we  don’t  have  only  1  entry  point   •  Also,  really  only  works  for  Groovy    
  66. Implementation  Strategies   •  Dynamic  bytecode  rewriEng   •  If

     there  is  1  factory  method  for  gekng  rx.Observables:   •  Can  rewrite  that  to  provide  an  rx.DynamicObservable  (which  only   exists  at  runEme  and  extends  rx.Observable)   •  rx.DynamicObservable  has  the  Object-­‐overloaded  method      public  <R>  DynamicObservable<R>  map(Object  f);      with  an  implementaEon  which  properly  delegates  to            language-­‐specific  adaptors   •  Needless  to  say,  this  is  really  complex  and  hard  to  debug   •  Also,  we  don’t  have  only  1  entry  point   •  Also,  really  only  works  for  Groovy    
  67. Implementation  Strategies   •  Give  up  

  68. Implementation  Strategies   •  Give  up  on  solving  this  in

     the  general  case  
  69. Implementation  Strategies   •  Give  up  on  solving  this  in

     the  general  case  
  70. Implementation  Strategies   •  Give  up  on  solving  this  in

     the  general  case   •  Instead,  solve  each  language  separately   •  Scala  –  implicit  conversions  and  value  class  
  71. Implementation  Strategies   •  Give  up  on  solving  this  in

     the  general  case   •  Instead,  solve  each  language  separately   •  Scala  –  implicit  conversions  and  value  class   •  Groovy  –  extension  modules  
  72. Implementation  Strategies   •  Give  up  on  solving  this  in

     the  general  case   •  Instead,  solve  each  language  separately   •  Scala  –  implicit  conversions  and  value  class   •  Groovy  –  extension  modules   •  Clojure  –  something  with  macros  (????)    
  73. Implementation  Strategies   •  Give  up  on  solving  this  in

     the  general  case   •  Instead,  solve  each  language  separately   •  Scala  –  implicit  conversions  and  value  class   •  Groovy  –  extension  modules   •  Clojure  –  something  with  macros  (????)   •  JRuby  –  Extension  methods  (I  think)  
  74. Implementation  Strategies   •  Give  up  on  solving  this  in

     the  general  case   •  Instead,  solve  each  language  separately   •  Scala  –  implicit  conversions  and  value  class   •  Groovy  –  extension  modules   •  Clojure  –  something  with  macros  (????)   •  JRuby  –  Extension  methods  (I  think)   •  Kotlin  –  just  received  this  -­‐  apparently  none  since  Kotlin  supports   Java  8  SAMs    
  75. Implementation  Strategies   •  Give  up  on  solving  this  in

     the  general  case   •  Instead,  solve  each  language  separately   •  Scala  –  implicit  conversions  and  value  class   •  Groovy  –  extension  modules   •  Clojure  –  something  with  macros  (????)   •  JRuby  –  Extension  methods  (I  think)   •  Kotlin  –  just  received  this  -­‐  apparently  none  since  Kotlin  supports   Java  8  SAMs   •  Lessons  learned   •  Leverage  each  language’s  specific  features  
  76. Implementation  Strategies   •  Give  up  on  solving  this  in

     the  general  case   •  Instead,  solve  each  language  separately   •  Scala  –  implicit  conversions  and  value  class   •  Groovy  –  extension  modules   •  Clojure  –  something  with  macros  (????)   •  JRuby  –  Extension  methods  (I  think)   •  Kotlin  –  just  received  this  -­‐  apparently  none  since  Kotlin  supports   Java  8  SAMs   •  Lessons  learned   •  Leverage  each  language’s  specific  features   •  Bytecode  generaEon  is  probably  not  the  correct  soluEon    
  77. Implementation  Strategies   •  Give  up  on  solving  this  in

     the  general  case   •  Instead,  solve  each  language  separately   •  Scala  –  implicit  conversions  and  value  class   •  Groovy  –  extension  modules   •  Clojure  –  something  with  macros  (????)   •  JRuby  –  Extension  methods  (I  think)   •  Kotlin  –  just  received  this  -­‐  apparently  none  since  Kotlin  supports   Java  8  SAMs   •  Lessons  learned   •  Leverage  each  language’s  specific  features   •  Bytecode  generaEon  is  probably  not  the  correct  soluEon   •  An  implementaEon  from  the  language  community  is  vastly   preferable  than  a  leaky  global  soluEon      
  78. Agenda   •  For  library  writers   •  How  does

     RxJava  support  Scala  in  parEcular?   •  How  does  RxJava  support  many  JVM  languages?   •  For  RxJava  consumers  (or  prospecEve  consumers)   •  How  do  other  concurrency  constructs  map  onto  Observable?   •  What  can  I  do  with  it?  
  79. Mapping  onto  Observable[T]   •  scala.collecEon.Iterable[T]   •  scala.concurrent.Future[T]  

    •  com.twi#er.uEl.Future[T]   •  akka.actor.Actor  
  80. Mapping  onto  Observable[T]   •  scala.collecEon.Iterable[T]   •  scala.concurrent.Future[T]  

    •  com.twi#er.uEl.Future[T]   •  akka.actor.Actor   •  rx.lang.scala.Observable  has  a  factory  method:          def  apply[T](func:  Observer[T]  =>  Subscription)  
  81. Iterable[T]   •  scala.collecEon.Iterable[T]   •  Need  to  implement:  

       def  apply[T](func:  Observer[T]  =>  Subscription)  
  82. Iterable[T]     def  toObservable[T](iterableT:  Iterable[T]):  Observable[T]  =    

         Observable((observer:  Observer[T])  =>  {              try  {                  iterableT.foreach(t  =>  observer.onNext(t))                  observer.onCompleted()              }  catch  {                  case  ex:  Throwable  =>  observer.onError(ex)              }                //no  unsubscribe              new  Subscription  {                  override  def  unsubscribe()  =  {}              }          })  
  83. Mapping  onto  Observable[T]   •  scala.uEl.concurrent.Future[T]   •  Need  to

     implement:      def  apply[T](func:  Observer[T]  =>  Subscription)  
  84. scala.concurrent.Future[T]   def  toObservable[T](futureT:  scala.concurrent.Future[T])  =        Observable((observer:

     Observer[T])  =>  {          import  ExecutionContext.Implicits.global            futureT.onComplete  {              case  Success(t)  =>  {                  observer.onNext(t)                  observer.onCompleted()                }              case  Failure(ex)  =>  observer.onError(ex)            }        //no  present  way  to  cancel  a  Scala  future      new  Subscription  {  override  def  unsubscribe()  =  {}  }   })  
  85. com.twitter.util.Future[T]   def  toObservable[T](futureT:  com.twitter.util.Future[T])  =        Observable((observer:

     Observer[T])  =>  {          futureT.onSuccess  {  t  =>  {              observer.onNext(t)              observer.onCompleted()          }          }.onFailure  {              ex  =>  observer.onError(ex)          }          new  Subscription  {      override  def  unsubscribe()  =  {        futureT.raise(new  FutureCancelledException)              }          }      })  
  86. Mapping  onto  Observable[T]   •  akka.actor.Actor  

  87. Mapping  onto  Observable   •  akka.actor.Actor   •  We’re  not

     sure  yet!   •  One  interesEng  direcEon  would  be  to  set  up  an  ActorScheduler   that  does  work  on  actors  instead  of  threads   •  More  to  come  on  this  –  if  you’re  interested,  we’d  love  some   input!  
  88. Demo:  HTTP  Client   •  Product  Goal:  Retrieve  all  menEons

     of  me  (@ma#rjacobs)  
  89. Demo:  HTTP  Client   •  Product  Goal:  Retrieve  all  menEons

     of  me  (@ma#rjacobs)   •  ImplementaEon:  Business  logic  on  top  of  rxjava-­‐apache-­‐h#p   •  Part  of  rxjava-­‐contrib  
  90. Demo:  HTTP  Client   •  Product  Goal:  Retrieve  all  menEons

     of  me  (@ma#rjacobs)   •  ImplementaEon:  Business  logic  on  top  of  rxjava-­‐apache-­‐h#p   •  REST:   •  h#ps://api.twi#er.com/1.1/statuses/menEons_Emeline.json   •  returns  JSON  blob  with  all  menEons    
  91. Demo:  HTTP  Client   •  Product  Goal:  Retrieve  all  menEons

     of  me  (@ma#rjacobs)   •  ImplementaEon:  Business  logic  on  top  of  rxjava-­‐apache-­‐h#p   •  REST:   •  h#ps://api.twi#er.com/1.1/statuses/menEons_Emeline.json   •  returns  JSON  blob  with  all  menEons   •  Seq[Mention]  /  Future[Seq[Mention]]  /  Observable[Mention]      
  92. Demo:  HTTP  Client   •  Product  Goal:  Retrieve  all  menEons

     of  me  (@ma#rjacobs)   •  ImplementaEon:  Business  logic  on  top  of  rxjava-­‐apache-­‐h#p   •  REST:   •  h#ps://api.twi#er.com/1.1/statuses/menEons_Emeline.json   •  returns  JSON  blob  with  all  menEons   •  Seq[Mention]  /  Future[Seq[Mention]]  /  Observable[Mention]   •  Streaming   •  h#ps://userstream.twi#er.com/1.1/user.json   •  returns  infinite  stream  of  JSON  using  Transfer-­‐Encoding:  chunked    
  93. Demo:  HTTP  Client   •  Product  Goal:  Retrieve  all  menEons

     of  me  (@ma#rjacobs)   •  ImplementaEon:  Business  logic  on  top  of  rxjava-­‐apache-­‐h#p   •  REST:   •  h#ps://api.twi#er.com/1.1/statuses/menEons_Emeline.json   •  returns  JSON  blob  with  all  menEons   •  Seq[Mention]  /  Future[Seq[Mention]]  /  Observable[Mention]   •  Streaming   •  h#ps://userstream.twi#er.com/1.1/user.json   •  returns  infinite  stream  of  JSON  using  Transfer-­‐Encoding:  chunked   •  Stream[Mention]  /  Observable[Mention]   •  In  both  cases,  Observable[Mention]  is  appropriate      
  94. Twitter  business  logic   •  case  class  Mention(time,  name,  screenName,

     imageUrl)  
  95. Twitter  business  logic   •  case  class  Mention(time,  name,  screenName,

     imageUrl)   •  def  getResponse(uri:  String):    Observable[ObservableHttpResponse]   •  ObservableHttp.createRequest(apacheReq,  client)  
  96. Twitter  business  logic   •  case  class  Mention(time,  name,  screenName,

     imageUrl)   •  def  getResponse(uri:  String):    Observable[ObservableHttpResponse]   •  def  getJson(resp:  ObservableHttpResponse):    Observable[Map[String,  Any]]   •  val  bytesObs    =  httpResp.getContent   •  val  stringObs  =  bytesObs.map(new  String(_))   •  val  jsonObs      =  stringObs.map(  //JSON  parsing)  
  97. Twitter  business  logic   •  case  class  Mention(time,  name,  screenName,

     imageUrl)   •  def  getResponse(uri:  String)  :          Observable[ObservableHttpResponse]   •  def  getJson(resp:  ObservableHttpResponse):    Observable[Map[String,  Any]]   •  def  getMention(m:  Map[String,  Any]):  Mention   •  val  userMap:  Map[String,  Any]  =  m("user”)   •  val  name  =  userMap("name”)   •  val  screenName  =  userMap("screen_name”)     •  val  imageUrl  =  userMap("profile_image_url”)   •  val  time  =  parseDate(m("created_at”)   •  Mention(time,  name,  screenName,  imageUrl)  
  98. Twitter  REST  Client   •  case  class  Mention(time,  name,  screenName,

     imageUrl)   •  def  getResponse(uri:  String)  :          Observable[ObservableHttpResponse]   •  def  getJson(resp:  ObservableHttpResponse):    Observable[Map[String,  Any]]   •  def  getMention(m:  Map[String,  Any]):  Mention                val  mentionObs:  Observable[Mention]  =  for  {          httpResp  <-­‐  getResponse(REST_URI)          jsonMap  <-­‐  getJson(httpResp)      }  yield  getMention(jsonMap)  
  99. Twitter  REST  Client   •  case  class  Mention(time,  name,  screenName,

     imageUrl)        val  mentionObs:  Observable[Mention]  =  for  {          httpResp  <-­‐  getResponse(REST_URI)          jsonMap  <-­‐  getJson(httpResp)      }  yield  getMention(jsonMap)     Result:     onNext  :  Mention(2013-­‐09-­‐24T00:26:22.000-­‐07:00,Alexy   Khrabrov,khrabrov,http://a0.twimg.com/profile_images/61382665/ Alexy_20080710_0999_normal.jpg)     onNext  :  Mention(2013-­‐09-­‐23T19:56:46.000-­‐07:00,Ben   Christensen,benjchristensen,http://a0.twimg.com/profile_images/ 3761650768/e602460a06e8a038b945e44e9df7585b_normal.jpeg)     onCompleted()  
  100. Twitter  Streaming  client   •  case  class  Mention(time,  name,  screenName,

     imageUrl)   •  def  getResponse(uri:  String)  :          Observable[ObservableHttpResponse]   •  def  getJson(resp:  ObservableHttpResponse):    Observable[Map[String,  Any]]   •  def  getMention(m:  Map[String,  Any]):  Mention              val  mentionObs:  Observable[Mention]  =  for  {          httpResp  <-­‐  getResponse(STREAMING_URI)          jsonMap  <-­‐  getJson(httpResp)  if   jsonMentionsMe(jsonMap,  "mattrjacobs")      }  yield  getMention(jsonMap)  
  101. DEMO   •  Tweet  at  @ma#rjacobs  

  102. Upcoming  Coursera  course   •  Principles  of  ReacEve  Programming  

    •  Taught  by:   •  MarEn  Odersky   •  Erik  Meijer   •  Roland  Kuhn   •  Starts  Nov  4   •  h#p://www.coursera.org/course/reacEve  
  103. Questions