1 / 68

Effective Scala

Effective Scala. HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3. $ whoami. Consultant since 1998 - mainly Java. Programming Scala since 2010. Been with Typesafe since 2011. Akka Honorary Team Member. Typesafe Console Tech Lead. Arsenal Supporter + Martial Artist. About Typesafe.

chibale
Download Presentation

Effective Scala

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Effective Scala • HENRIK ENGSTRÖM • SOFTWARE ENGINEER - TYPESAFE • @h3nk3

  2. $ whoami • Consultant since 1998 - mainly Java • Programming Scala since 2010 • Been with Typesafe since 2011 • Akka Honorary Team Member • Typesafe Console Tech Lead • Arsenal Supporter + Martial Artist

  3. About Typesafe • The Typesafe Platform • Play, Akka, Scala, Scala IDE, Slick, SBT, etc. • Subscription • Training and Consulting

  4. Agenda • Basic Stuff • Object Orientation in Scala • Implicits • Types • Collections • Pattern Matching • Functional Programming

  5. Basic Stuff

  6. // REPL = Read Eval Print Loop $scala_home/bin/scala WelcometoScalaversion2.10.0 scala>println("Hello, world!") Hello,world! scala> Use the REPL

  7. // Put JAR files in scala_home/lib to get access $scala_home/bin/scala WelcometoScalaversion2.10.0 scala>importcom.x.y.z.MyClass scala>valinstance=newMyClass scala>instance.myMethod The REPL and JARs

  8. REPL 2013 • IDE Worksheet • Scala IDE : awesome • IntelliJ : okay

  9. // JAVA Stringresult=null; if(z<9)result="<9"elseresult=">=9"; System.out.println("Result: "+result); // SCALA println("Result: "+if(z<9)"<9"else">=9")) Expression vs Statement

  10. // What type is variable quiz? varx=1 valquiz=while(x<10){ println("X is: "+x) x+=1 } Statement Pop Quiz • All expressions in Scala returns a type

  11. // Don’t tell the computer how to fish importscala.collection.mutable.{HashSet=>MHS} deffindPeopleInCity(c:String,ppl:Seq[People]): Set[People]={ valfound=newMHS[People]() for(p<-ppl) for(a<-p.address) if(a.city==c)found.put(p) found } No Fishing Instructions Please!

  12. deffindPeopleInCity(c:String,ppl:Seq[People]): Set[People]={ for{ p<-ppl.toSet[People] a<-p.address ifa.city==c }yieldp } // SQL LIKE SYNTAX; FROM, WHERE, AND, SELECT Instead - Just Order Fish

  13. // 1) Mutable code leads to cloning // 2) Cloning leads to performance degradation // => Mutable code leads to worse performance classFootballer{ privatevarcars=Array[Car]() defsetCars(c:Array[Car]):Unit= cars=c.clone defgetCars:Array[Car]=cars.clone } Stay Immutable

  14. // Safer code - use immutable collection classFootballer{ privatevarcars=Vector.empty[Car] defsetCars(c:Vector[Car])=cars=c defgetCars:Vector[Car]=cars } Stay Immutable

  15. // Case classes make the class immutable scala>caseclassCar(brand:String) scala>caseclassFootballer(name:String,team: String,cars:Vector[Car]=Vector.empty) scala>varjw=newFootballer("Jack Wilshire", "Arsenal") Footballer(JackWilshire,Arsenal,Vector()) scala>jw=jw.copy(cars=Vector(Car("Porsche"))) Footballer(JackWilshire,Arsenal, Vector(Car(Porsche))) Case Classes ftw

  16. Immutable Benefits • Simple equality • Simple hashcode • No need to lock • No defensive copying • Scala Case Classes • Automatic equality + hashcode (Murmur) • Lots of other goodies (e.g. copy)

  17. // Sometimes local mutability makes sense importscala.collection.mutable.ArrayBuffer classMyClass{ deftheData:Seq[Int]={ valbuffer=newArrayBuffer[Int]() populateData(buffer) buffer.toSeq } } Local Mutability

  18. // A classic game of null defauth(usr:String,pwd:String):Privileges= if(usr==null||usr.isEmpty|| pwd==null||pwd.isEmpty|| !validCredentials(usr,pwd)) withPrivileges(Anonymous) else privilegesFor(usr) Use Option

  19. defauth(usr:Option[String],pwd:Option[String]): Privileges={ valprivileges:Option[Privileges]={ u<-usr p<-pwd if(!u.isEmpty&&!p.isEmpty) ifcanAuthenticate(u,p) }yieldprivilegesFor(u) privilegesgetOrElsewithPrivileges(Anonymous) } Use Option

  20. Object Orientation

  21. traitSquareShape{ valwidth:Int valheight:Int valarea:Int=width*height } classRect(w:Int,h:Int)extendsSquaredShape{ overridevalwidth=w overridevalheight=h } scala>valr1=newRectangle(1,314) scala>r1.height res0:Int=314 scala>r1.area res1:Int=0 val for abstract members

  22. Scala Initialization Order • From the Scala specification (section 5.1) • http://www.scala-lang.org/docu/files/ScalaReference.pdf • First, the superclass constructor is evaluated • Then, all base classes in the template’s linearization ... • Finally the statement sequence stats is evaluated

  23. traitSquareShape{ defwidth:Int defheight:Int defarea:Int=width*height } classRect(w:Int,h:Int)extendsSquaredShape{ overridevalwidth=w overridevalheight=h } // or even better caseclassRect(width:Int,height:Int)extends SquaredShape def is much better

  24. // Non-trivial return types should always be // annotated! defconvert(x:Int)=xmatch{ case1=>1.toChar case2=>true casez=>z.toByte } defconvert(x:Int):AnyVal=xmatch{ Annotate your APIs

  25. Composition and Inheritance • Prefer composition over inheritance • easier to modify (e.g. DI) • Composition can use inheritance in Scala • leads to the famous cake pattern

  26. traitUserRepoComponent{ defuserLocator:UserLocator defuserUpdater:UserUpdater traitUserLocator{ deffindAll:Vector[User] } traitUserUpdater{ defsave(user:User) } } Let’s bake a cake

  27. traitJPAUserRepoComponentextendsUserRepoComponent{ defem:EntityManager defuserLocator=newJPAUserLocator(em) defuserUpdater=newJPAUserUpdater(em) classJPAUserLocator(em:EntityManager)extendsUserLocator{ deffindAll:Vector[User]=em.createQuery("from User", classOf[User]).getResultList.toVector } classJPAUserUpdater(em:EntityManager)extendsUserUpdater{ defsave(user:User)=em.persist(user) } } Baking in process

  28. traitUserServiceComponent{ defuserService:UserService traitUserService{ deffindAll:Vector[User] defsave(user:User):Unit defcheckStatusOf(user:User):String } } Service Layer

  29. traitDefaultUserServiceComponentextends UserServiceComponent{ this:UserRepositoryComponent=> defuserService=newDefaultUserService classDefaultUserServiceextendsUserService{ deffindAll=userLocator.findAll defsave(user:User)=userUpdater.save(user) defcheckStatus(user:User)= s"User $user seems okay to me" } } Service Layer Implementation

  30. objectMyApplicationextendsApplication{ • valcompService= • newDefaultUserServiceComponent • withJPAUserRepositoryComponent{ • defem= • Persistence.createEntityManagerFactory( • "cakepattern").createEntityManager() • } • valservice=compService.userService • // use the service • } Use it

  31. classMyTestextendsWordSpecwithMustMatcherswith Mockito{ traitMockedEntityManager{ defem=mock[EntityManager] } "service"must{ "return all users"in{ valcompService= newDefaultUserServiceComponent withJPAUserRepositoryComponent withMockedEntityManager // perform tests } } Test it

  32. Implicits

  33. What is it good for? • Removes boilerplate code in a specific context • compile time safety • must be unambiguous though

  34. traitAutoRepository{ deffind(regId:String)(implicitdbId:DBId): Option[Car] deffindAll(country:String)(implicitdbId:DBId): Seq[Car] } classDefaultAutoRepositoryextendsAutoRepository{ deffind(regId:String)(implicitdbId:DBId): Option[Car]={// ... } deffindAll(country:String)(implicitdbId:DBId): Seq[Car]={// ... } } Example

  35. // Anti pattern classCarFinder{ valdbId=DbId("Dealer1") valrepo=newDefaultAutoRepository defgetCar(regId:String):Option[Car]= repo.find(regId)(dbId) deflistCars(country:String):Seq[Car]= repo.findAll(country)(dbId) } Example continued

  36. // Use implicits => much cleaner code classCarFinder{ implicitvaldbId=DbId("Dealer1") valrepo=newDefaultAutoRepository defgetCar(regId:String):Option[Car]= repo.find(regId) deflistCars(country:String):Seq[Car]= repo.findAll(country) } Example continued

  37. Compiler workout • Implicit scope • Lexical : current scope, explicit imports, wildcard imports • Companions of parts of the type : companion of types, companion of types of arguments, outer objects of nested types • Can be expensive in compile time - use with care

  38. traitLogger{deflog(msg:String)} objectLogger{ implicitobjectDefaultLoggerextendsLogger{ deflog(msg:String)=println("DL> "+msg) } deflog(msg:String)(implicitlogger:Logger)={ logger.log(msg) } } Implicit Values

  39. scala>Logger.log("a small test") DL> a small test scala>classMyLoggerextendsLogger{ deflog(msg:String)=println("ML:>> "+msg) } scala>implicitdefmyLogger=newMyLogger scala>Logger.log("another test") ML:>> another test Implicit Values

  40. Implicits wisdom? deech @deech Debugging #scala implicits is like trying to find the farter in a crowed room

  41. Type Traits

  42. // "describes generic interfaces using type // parameters such that the implementations can // be created for any type" traitEncodable[T]{ deffrom(t:T):String defto(s:String):T } objectEncodable{ implicitobjectIntEncoderextendsEncodable[Int]{ deffrom(i:Int):String="int"+i defto(s:String):Int= s.substring(s.indexOf("int")+3,s.length).toInt } } a.k.a Type Classes

  43. classMyHandler{ defconvert[T](t:T)(implicitenc:Encodable[T]): String=enc.from(t) defconvert[T](s:String)(implicitenc: Encodable[T]):T=enc.to(s) } scala>valmyHandler=newMyHandler scala>myHandler.convert(12345) res0:String=int12345 scala>myHandler.convert(res0) res1:Int=12345 Example Usage

  44. scala>myHandler.convert(12345L) <console>:15: error: could not find implicit value for parameter encoder: Encodable[Long] scala>implicitobjectLongEncextends Encodable[Long]{ deffrom(l:Long):String="long"+l defto(s:String):Long= s.substring(s.indexOf("long")+4, s.length).toLong} scala>myHandler.convert(12345L) Example continued

  45. Collections

  46. Collections Overview

  47. Immutable Collections

  48. Mutable Collections

  49. // It is absolutely *awesome* scala>valseq=Seq() scala>seq. ++ ++: +: /: /:\ :+ :\ addString aggregate andThen apply applyOrElse asInstanceOf canEqual collect collectFirst combinations companion compose contains containsSlice copyToArray copyToBuffer corresponds count diff distinct drop dropRight dropWhile endsWith exists filter filterNot find flatMap flatten fold foldLeft foldRight forall foreach genericBuilder groupBy grouped hasDefiniteSize head headOption indexOf indexOfSlice indexWhere indices init inits intersect isDefinedAt isEmpty isInstanceOf isTraversableAgain iterator last lastIndexOf lastIndexOfSlice lastIndexWhere lastOption length lengthCompare lift map max maxBy min minBy mkString nonEmpty orElse padTo par partition patch permutations prefixLength product reduce reduceLeft reduceLeftOption reduceOption reduceRight reduceRightOption repr reverse reverseIterator reverseMap runWith sameElements scan scanLeft scanRight segmentLength seq size slice sliding sortBy sortWith sorted span splitAt startsWith stringPrefix sum tail tails take takeRight takeWhile to toArray toBuffer toIndexedSeq toIterable toIterator toList toMap toSeq toSet toStream toString toTraversable toVector transpose union unzip unzip3 updated view withFilter zip zipAll zipWithIndex Explore and learn the API

  50. Message to Java Developers • Use Vector not List • it is faster • it is more memory efficient

More Related