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

5 Bullets to Scala Adoption

5 Bullets to Scala Adoption

A talk given at JEEConf 2015 in Kiev, Ukraine.

So you've heard of this newfangled "Scala" thing and think it might be worth checking out. Good for you! Unfortunately, it's also daunting. Your first search for Scala is likely to pitch it as a "statically-typed hybrid object-oriented and functional language", which is technically true but doesn't really help much. Instead, this talk will provide an overview of the language, focusing on five highly practical advantages Scala has over Java without a brain hemorrhage-inducing paradigm shift, followed by some tips and recommendations on Scala adoption in the real world and (as time allows) open-ended Q&A.

Tomer Gabel

May 22, 2015
Tweet

More Decks by Tomer Gabel

Other Decks in Programming

Transcript

  1. Agenda   The Pitch   Ecosystem   Community   Velocity

      Peopleware Yourself Your team Your boss
  2. Why should you care? Me •  System architect at Wix

    •  Using Scala since 2011 •  User group leader: –  Underscore (Scala) –  Java.IL (Java/JVM) •  Organizer of Scalapeño Wix •  >60M users •  Using Scala since 2012 •  The usual suspects: –  Microservices –  SLAs –  Legacy Java codebase
  3. This is Spar– I mean, Java public  class  Person  {

             private  String  name;          private  String  surname;          private  int  age;            public  Person(String  name,  String  surname,  int  age)  {                  this.name  =  name;                  this.surname  =  surname;                  this.age  =  age;          }            public  String  getName()  {  return  name;  }          public  String  getSurname()  {  return  surname;  }          public  int  getAge()  {  return  age;  }            @Override          public  String  toString()  {  /*  …  */  }   }  
  4. In Java-land public  class  SimplePersonGenerator  {        

       //  Prepare  a  bunch  of  useful  constants…            private  static  List<String>  names;          private  static  List<String>  surnames;            static  {                  names  =  new  ArrayList<>();                  names.add("Jeffrey");                  names.add("Walter");                  names.add("Donald");                    surnames  =  new  ArrayList<>();                  surnames.add("Lebowsky");                  surnames.add("Sobchak");                  surnames.add("Kerabatsos");          }  
  5. In Java-land        //  Continued  from  previous  slide

               private  static  Random  random  =  new  Random();            public  static  Person  generatePerson(int  age)  {                  String  name  =  names.get(random.nextInt(names.size()));                  String  surname  =  surnames.get(random.nextInt(surnames.size()));                  return  new  Person(name,  surname,  age);          }            public  List<Person>  generatePeople(int  count,  int  age)  {                  List<Person>  people  =  new  ArrayList<>(count);                  for  (int  i  =  0;  i  <  count;  i++)                          people.add(generatePerson(age));                  return  Collections.unmodifiableList(people);          }  
  6. Scala 101 Java public  class  Person  {      

       private  String  name;          private  String  surname;          private  int  age;            public  Person(String  name,  String  surname,                                      int  age)  {                  this.name  =  name;                  this.surname  =  surname;                  this.age  =  age;          }            public  String  getName()  {  return  name;  }          public  String  getSurname()  {  return  surname;  }          public  int  getAge()  {  return  age;  }            @Override          public  String  toString()  {  /*  …  */  }   }   Scala   case  class  Person(      name:  String,      surname:  String,      age:  Int)     Provides: –  Constructor –  Getters –  hashCode/equals –  toString
  7. Scala 101 Java public  class  SimplePersonGenerator  {      

         //  Prepare  a  bunch  of  useful  constants…            private  static  List<String>  names;          private  static  List<String>  surnames;            static  {                  names  =  new  ArrayList<>();                  names.add("Jeffrey");                  names.add("Walter");                  names.add("Donald");                    surnames  =  new  ArrayList<>();                  surnames.add("Lebowsky");                  surnames.add("Sobchak");                  surnames.add("Kerabatsos");          }   Scala object  NaiveScalaPersonGenerator  {        private  val  names:  List[String]  =          List(              "Jeffrey",              "Walter",              "Donald"            )        private  val  surnames:  List[String]  =          List(              "Lebowsky",              "Sobchak",              "Kerabatsos"            )   First-class singletons
  8. Scala 101 Java private  static  Random  random  =  new  Random();

        public  static  Person  generatePerson(int  age)  {      String  name  =            names.get(random.nextInt(names.size()));              String  surname  =          surnames.get(random.nextInt(surnames.size()));      return  new  Person(name,  surname,  age);   }     public  List<Person>  generatePeople(          int  count,  int  age)  {      List<Person>  people  =  new  ArrayList<>(count);      for  (int  i  =  0;  i  <  count;  i++)          people.add(generatePerson(age));      return  Collections.unmodifiableList(people);   }   Scala private  val  random:  Random  =  new  Random     def  generatePerson(age:  Int):  Person  =  {      val  name:  String  =          names(random.nextInt(names.size))      val  surname:  String  =          surnames(random.nextInt(surnames.size))      new  Person(name,  surname,  age)   }     def  generatePeople(count:  Int,  age:  Int):          List[Person]  =  {      val  people:  mutable.ListBuffer[Person]  =          mutable.ListBuffer.empty      for  (i  <-­‐  0  until  count)          people.append(generatePerson(age))      people.result()   }   Everything is an expression Comprehensions
  9. We started off with: object  NaiveScalaPersonGenerator  {      

     private  val  names:  List[String]  =  List("Jeffrey",  "Walter",  "Donald")      private  val  surnames:  List[String]  =  List("Lebowsky",  "Sobchak",  "Kerabatsos")      private  val  random:  Random  =  new  Random        def  generatePerson(age:  Int):  Person  =  {          val  name:  String  =  names(random.nextInt(names.size))          val  surname:  String  =  surnames(random.nextInt(surnames.size))          new  Person(name,  surname,  age)      }        def  generatePeople(count:  Int,  age:  Int):  List[Person]  =  {          val  people:  mutable.ListBuffer[Person]  =  mutable.ListBuffer.empty          for  (i  <-­‐  0  until  count)              people.append(generatePerson(age))          people.result()      }   }
  10. Type inference object  NaiveScalaPersonGenerator  {        private  val

     names:  List[String]  =  List("Jeffrey",  "Walter",  "Donald")      private  val  surnames:  List[String]  =  List("Lebowsky",  "Sobchak",  "Kerabatsos")      private  val  random:  Random  =  new  Random        def  generatePerson(age:  Int):  Person  =  {          val  name:  String  =  names(random.nextInt(names.size))          val  surname:  String  =  surnames(random.nextInt(surnames.size))          new  Person(name,  surname,  age)      }        def  generatePeople(count:  Int,  age:  Int):  List[Person]  =  {          val  people:  mutable.ListBuffer[Person]  =  mutable.ListBuffer.empty          for  (i  <-­‐  0  until  count)              people.append(generatePerson(age))          people.result()      }   }
  11. Type inference object  NaiveScalaPersonGenerator  {        private  val

     names  =  List("Jeffrey",  "Walter",  "Donald")      private  val  surnames  =  List("Lebowsky",  "Sobchak",  "Kerabatsos")      private  val  random  =  new  Random        def  generatePerson(age:  Int):  Person  =  {          val  name  =  names(random.nextInt(names.size))          val  surname  =  surnames(random.nextInt(surnames.size))          new  Person(name,  surname,  age)      }        def  generatePeople(count:  Int,  age:  Int):  List[Person]  =  {          val  people  =  mutable.ListBuffer.empty          for  (i  <-­‐  0  until  count)              people.append(generatePerson(age))          people.result()      }   }
  12. Collections //  Some  data  to  start  with…   val  people

     =  generatePeople(5,  42)     val  names  =  people.map(p  =>  p.name)     val  adults  =  people.filter(p  =>  p.age  >=  18)     //  Functions  are  1st  class  citizens   def  isAdult(p:  Person)  =  p.age  >=  18   val  adults2  =  people.filter(isAdult)   assert(adults2  ==  adults)  
  13. Collections val  youngest  =  people.minBy(p  =>  p.age)     val

     oldest  =  people.maxBy(p  =>  p.age)     val  hasAdults  =  people.exists(p  =>  p.age  >=  18)     val  (adults,  minors)  =  people.partition(p  =>  p.age  >=  18)     val  randomPairs  =  Random.shuffle(people).grouped(2)    
  14. Collections val  trivial:  Map[String,  String]  =      Map("name"  

     -­‐>  "Jeffrey  Lebowsky",              "alias"  -­‐>  "The  Dude”)     val  directory:  Map[Char,  List[Person]]  =      people          .groupBy(p  =>  p.surname.head.toUpper)          .withDefaultValue(List.empty)     val  beginningWithK:  List[Person]  =  directory('K’)     val  countByFirstLetter:  Map[Char,  Int]  =        directory.mapValues(list  =>  list.size)  
  15. Options case  class  PartiallyKnownPerson(name:        Option[String]  =  None,

                                                                     surname:  Option[String]  =  None,                                                                  age:          Option[Int]        =  None)     val  person  =  PartiallyKnownPerson(name  =  Some("Brandt"))         val  explicitAccess  =  person.age.get      //  <-­‐-­‐  NoSuchElementException  thrown  here         val  withDefault  =  person.name.getOrElse("Anonymous")   println("Hello  "  +  withDefault  +  "!")     //  Options  are  also  zero-­‐  or  single-­‐element  collections!   person.name  foreach  {  n  =>  println("hello"  +  n)  }   Named Parameters
  16. Traits import  org.slf4j.Logger;   import  org.slf4j.LoggerFactory;     public  class

     ClassWithLogs  {          private  static  Logger  log  =  LoggerFactory.getLogger(ClassWithLogs.class);            public  String  getNormalizedName(Person  person)  {                  log.info("getNormalizedName  called");                  log.debug("Normalizing  "  +  person.toString());                  String  normalizedName  =  person.getName().toUpperCase().trim();                  log.debug("Normalized  name  is:  "  +  normalizedName);                  return  normalizedName;          }   } Eager Evaluation Boilerplate
  17. Traits Usage   class  ClassWithLogs  extends  Logging  {    

       def  normalizeName(person:  Person)  =  {          logInfo("normalizeName  called")          logDebug("Normalizing  "  +  person)          val  result  =              person.name.toUpperCase.trim          logDebug("Normalized:  "  +  result)          result      }   } Definition   trait  Logging  {      private  val  log  =          LoggerFactory.getLogger(this.getClass)        protected  def  logInfo(message:  =>String)  =            if  (log.isInfoEnabled)  log.info(message)        protected  def  logDebug(message:  =>String)  =            if  (log.isDebugEnabled)  log.debug(message)        //  ...   } Lazy Evaluation
  18. DSLs class  StackTest  extends  Specification  {        "Seq.head"

     should  {          "throw  an  exception  if  empty"  in  {              Seq.empty[String].head  should                  throwA[NoSuchElementException]          }            "return  the  first  element"  in  {              Seq(1,  2,  3).head  shouldEqual  1          }      }   }
  19. DSLs val  orders  =  List[Order](      new  Order  to

           buy(100  sharesOf  "IBM")      maxUnitPrice  300      using  premiumPricing,        new  Order  to        sell(200  bondsOf  "Sun")      maxUnitPrice  300      using  {  (qty,  unit)  =>          qty  *  unit  –  500      }   ) class  StackTest  extends  Specification  {        "Seq.head"  should  {          "throw  an  exception  if  empty"  in  {              Seq.empty[String].head  should                  throwA[NoSuchElementException]          }            "return  the  first  element"  in  {              Seq(1,  2,  3).head  shouldEqual  1          }      }   }
  20. What about Java 8, you ask? •  Java 8 is

    awesome •  It validates Scala! •  But it’s not up to par –  Closures/lambdas –  Default methods
  21. Note that we haven’t covered… •  For comprehensions •  Lazy

    vals •  Tuples •  Generics –  Type bounds –  Variance –  Higher-kinded types •  Implicits –  Type classes –  Conversions –  Parameters •  Functions •  Top and bottom types •  Scoped imports •  Extractor objects •  Structural types •  Type members •  Path-dependent types •  Futures and promises •  Macros
  22. Not just servers! JavaScript Android •  Two major libraries: Macroid

    and Scaloid •  Both provide: –  DSL for layouts –  Interop with Java code –  sbt integration •  Overhead: 10-500KB •  JavaScript backend provided by Scala.js –  Non-experimental –  Bidirectional interop –  Source maps –  Output ranges 170-400KB
  23. User groups •  ScalaUA – Ukrainian user group, founded in 2011

    – 250 members http://scala-lang.org.ua/ https://www.facebook.com/groups/scala.ua/
  24. Step Up Your Game •  New tech is scary – 

    Migration path –  The dreaded rewrite –  Impedance mismatch –  Onboarding •  It’s all about risk
  25. 1. Gateway Drug •  Scala rocks your tests! –  Expressive

    syntax –  DSL support –  Excellent libraries •  Trivial integration –  Ant/Maven/Gradle… –  Non-production code! class  StackTest  extends  Specification  {        "Seq.head"  should  {          "throw  an  exception  if  empty"  in  {              Seq.empty[Any].head  should                  throwA[NoSuchElementException]          }            "return  the  first  element"  in  {              Seq(1,  2,  3).head  shouldEqual  1          }      }   }
  26. 2. Greenfield project •  Applicable for: –  New projects – 

    Self-contained –  Experimental/non- critical •  Assume 3-4 weeks’ overhead
  27. 3. The layered approach Components Frameworks Deployment Executable Container Crawler

    framework Twitter TripAdvisor OpenTable Web framework Dashboard Backoffice
  28. 4. The silo approach Components Frameworks Deployment Executable Container Crawler

    framework Twitter TripAdvisor OpenTable Web framework Dashboard Backoffice
  29. Pilot projects “A pilot project is one in which you…

    try some new and unproved technique… you can expect to be inefficient at the start. On the other side… is the improvement in productivity... [and] the Hawthorne Effect.” - Chapter 24, “Peopleware”
  30. The Hawthorne Effect “A type of reactivity in which individuals

    improve an aspect of their behavior in response to their awareness of being observed” - Wikipedia
  31. The Hawthorne Effect “A type of reactivity in which individuals

    improve an aspect of their behavior in response to their awareness of being observed” - Wikipedia “Uncontrolled novelty effects cause on average 30% of a standard deviation (SD) rise” - Clark & Sugrue "Research on instructional media, 1978-1988”