Introduction To Scala

  • 460 views
Uploaded on

This presentation is meant primarily for Java developers.

This presentation is meant primarily for Java developers.

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
460
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
19
Comments
0
Likes
2

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. A SCALABLE LANGUAGE Innar Made
  • 2. TOPICS JVM languages landscape OOP FP Pattern matching Pimp my library XML Parallel collections Actors Scala in real world
  • 3. JVMLANGUAGES LANDSCAPE What else is there besides Java?
  • 4. JAVAPROSANDCONS Pros Simple language Portable Lot's of libraries and frameworks Big community Cons Class based OOP is used for everything, even when inappropriate, due to lack of support for anything else Lot's of boilerplate code Favors mutability
  • 5. WHYTRYOTHER JVM LANGUAGES? Those languages tend to be more expressive - say more, write less Discover new ways of thinking about problems Use the existing Java libraries/frameworks to fill in the gaps
  • 6. MEETSCALA
  • 7. OVERVIEW Created by Martin Odersky Appeared around 2003 Statically typed Type inference Mixture of OOP and FP Actor concurrency Very good Java interop Currently the most widely adopted alternate JVM language
  • 8. OOPIN SCALA
  • 9. PERSON IN JAVA import static java.lang.String.format; public class Person { public final String name; public final int age; public Person(final String name, final int age) { this.name = name; this.age = age; } public String greet() { return format("Hello, my name is %s", name); } public String greet(final Person person) { return format("Hello, %s, my name is %s", person.name, name); } @Override public String toString() { return format("name=%s, age=%d", name, age); } }
  • 10. SAMEPERSON IN SCALA class Person(val name: String, val age: Int) { def greet = "Hello, my name is %s".format(name) def greet(person: Person) = "Hello, %s, my name is %s".format(person.name, name) override def toString = "name=%s, age=%d".format(name, age) }
  • 11. JOE,MEETJOE val joe = new Person("Joe", 35) val age = joe.age val olderJoe = new Person(joe.name, age + 1) joe.greet(olderJoe) joe greet olderJoe
  • 12. TYPEINFERENCE Scala code looks a bit like scripting language, right? That is because Scala compiler can sometimes infer the types. In that case you don't need to declare types explicitly. But you can, if you want. val joe = new Person("Joe", 35) val age = joe.age
  • 13. WITHEXPLICITTYPES val joe: Person = new Person("Joe", 35) val age: Int = joe.age val olderJoe: Person = new Person(joe.name, age + 1) joe greet olderJoe def makeFriends(p1: Person, p2: Person): String = "%s and %s became good friends".format(p1.name, p2.name) makeFriends(joe, olderJoe)
  • 14. INHERITANCE abstract class A { def call = "A" } trait B { def call = "B" } trait C { def call = "C" } class ABC extends A with B with C { override def call = super.call } val abc = new ABC println(abc.call) //prints C
  • 15. ANONYMOUSCLASSES trait Jedi { def useForceOn(p: Person): String def greet(p: Person) = "May the force be with you, %s!".format(p.name) } trait ForceLightning { def useForceOn(p: Person) = "%s gets shocked with force lightning" } val obiWan = new Person("Obi Wan", 71) with Jedi { override def greet(p: Person) = super.greet(p) def useForceOn(p: Person): String = "These are not the droids you're looking for, %s".format(p.name) } val anakin = new Person("Anakin", 27) with Jedi with ForceLightning { override def greet(p: Person) = "Join the dark side, %s!".format(p.name) }
  • 16. GENERICS class Pair[A,B](val first: A, val second: B) { def add[C](third: C) = new Triple(first, second, third) } class Triple[A,B,C](val first: A, val second: B, val third: C) val p = new Pair("Joe", 36) val t = p.add('MALE)
  • 17. COMPANION OBJECT class Pair[A, B](val first: A, val second: B) object Pair { def apply[A, B](first: A, second: B) = new Pair(first, second) def empty[A, B] = new Pair[Option[A], Option[B]](None, None) } //Pair[String, Int] val p1 = Pair("Joe", 36) //Pair[Option[String], Option[Int]] val p2 = Pair.empty[String, Int]
  • 18. FUNCTIONAL PROGRAMMING
  • 19. FP Using side effect free functions for computations Avoiding mutable data structures and variables First-class functions Recursion
  • 20. FPBENEFITS Side effect free functions are easy to test and understand. Functions are more reusable than classes Makes concurrency and parallelism easier More declarative and concise code Stop worrying what objects got mutated between method calls and instead start combining functions to transform the input to the desired output
  • 21. OOP+FP OOP generally is about nouns (Person, Car, Record) FP is about verbs (filter, map, reduce, group, zip) OOP offers a way to structure FP offers a way to compute In large projects we benefit from both
  • 22. FPUSECASES Good fit: Transforming data Systems where you need concurrency and parallelism Not a good fit: UI (although, rumor has it that FRP makes UI programming easy) IO (read/write file, query the DB, make a HTTP request) PS! Above scenarios are all good fit for Scala as you can easily change programming paradigms
  • 23. FIRST-CLASSFUNCTIONS
  • 24. FUNCTIONSAS PARAMETERS filter, map and reduce are functions defined on the List object filter expects a function of A => Boolean map expects a function of A => B reduce expects a function of (A, A) => A def isEven(x: Int) = x % 2 == 0 def pow3(x: Int) = x * x * x def add(x: Int, y: Int) = x + y val numbers = List(1, 2, 3, 4) numbers.filter(isEven).map(pow3).reduce(add) //72
  • 25. FUNCTION LITERALS All FP languages support function literals since it's the central part of the paradigm val numbers = List(1, 2, 3, 4) val evens = numbers.filter(n => n % 2 == 0) val powOf3s = evens.map(n => n * n * n) val sum = powOf3s.reduce((prev, next) => prev + next)
  • 26. PLACEHOLDERS You can use underscores as placeholders for one or more parameters, so long as each parameter appears only one time within the function literal. val numbers = List(1, 2, 3, 4) val evens = numbers.filter(_ % 2 == 0) val powOf3s = evens.map(n => n * n * n) // here it's necessary val sum = powOf3s.reduce(_ + _)
  • 27. ASSIGNINGFUNCTIONSTO VALUES val evens = (_: Int) % 2 == 0 val pow3 = (n: Int) => n * n * n val add = (x: Int, y: Int) => x + y val numbers = List(1, 2, 3, 4) val result = numbers.filter(evens).map(pow3).reduce(add)
  • 28. FUNCTIONSAREOBJECTS TOO Every function implements the FunctionN trait. The apply method allows us to use the function syntax. val f = new Function2[Int, Int, Int] { def apply(y: Int, x: Int): Int = x + y } f(1, 2) ((x: Int, y: Int) => x + y).isInstanceOf[Function2[Int, Int, Int]]
  • 29. FUNCTIONSTHATRETURN FUNCTIONS It's quite common to write a function that produces a new function def powerOf(p: Int) = (n: Int) => Math.pow(n, p).toInt val powerOf5 = powerOf(5) val numbers = List(1, 2, 3, 4) numbers.map(powerOf5) //1, 32, 243, 1024 numbers.map(powerOf(2)) //1, 4, 9, 16
  • 30. INNER FUNCTIONS You can write functions inside functions def funkyName(name: String) = { def replaceLettersWithNumbers(s: String) = { val numberMap = Map( 'i' -> '1', 'l' -> '1', 'e' -> '3', 'a' -> '4', 's' -> '5', 'o' -> '0') s.map(ltr => numberMap.getOrElse(ltr, ltr)) } replaceLettersWithNumbers(name.toLowerCase) } println(funkyName("Martin Odersky")) //m4rt1n 0d3r5ky
  • 31. EVERYDAYEXAMPLES The usual stuff you code almost every day.
  • 32. EVERYDAYEXAMPLES Transforming Collections from type A to B Java: public Collection<String> personsToNames(Collection<Person> persons) { List<String> names = new ArrayList<String>(); for (Person p : persons) { names.add(p.name); } return names; }
  • 33. EVERYDAYEXAMPLES Transforming Collections from type A to B Scala: persons.map(_.name)
  • 34. EVERYDAYEXAMPLES Filter elements from Collections based on criteria Java: public Collection<Person> filterAges(Collection<Person> persons, int threshol List<Person> filtered = new ArrayList<Person>(); for (Person p : persons) { if (p.age >= threshold) { filtered.add(p); } } return filtered; }
  • 35. EVERYDAYEXAMPLES Filter elements from Collections based on criteria Scala: persons.filter(_.age >= 30)
  • 36. EVERYDAYEXAMPLES Aggregation Java: public Integer averageAge(Collection<Person> persons) { int ageSum = 0; for (Person p : persons) { ageSum += p.age; } return ageSum / persons.size(); }
  • 37. EVERYDAYEXAMPLES Aggregation Scala: persons.foldLeft(0)((sum, p) => sum + p.age) / persons.size
  • 38. EVERYDAYEXAMPLES Grouping Java: public Map<Integer, Collection<Person>> ageGroups( Collection<Person> persons) { Map<Integer, Collection<Person>> groups = new HashMap<Integer, Collection<Person>>(); for (Person p : persons) { if (groups.containsKey(p.age)) { groups.get(p.age).add(p); } else { final ArrayList<Person> coll = new ArrayList<Person>(); coll.add(p); groups.put(p.age, coll); } } return groups; }
  • 39. EVERYDAYEXAMPLES Grouping Scala: persons.groupBy(_.age)
  • 40. RECURSION Procedural way to calculate factorial FP avoids loops with mutable state. Instead recursion is often used. def factorial(n: BigInt) = { if (n == 0) 1 else { var product: BigInt = 1 var i = n while (i > 0) { product *= i i -= 1 } product } } def factorial(n: BigInt): BigInt = if (n == 0) 1 else n * factorial(n - 1)
  • 41. TAILCALLOPTIMIZATION Previous recursive factorial consumes stack and will throw StackOverflow for big numbers. A proper FP language offers tail call optimization. In Scala we can use the @tailrec annotation. The compiler will complain if it is unable to make the function tail recursive. def factorial(n: BigInt) = { @tailrec def calc(n: BigInt, acc: BigInt): BigInt = if (n == 0) acc else calc(n - 1, acc * n) calc(n, 1) }
  • 42. FOR EXPRESSION The for expression is a powerful tool in Scala that allows you to: iterate through a collection perform nested iterations filter out elements based on arbitrary conditions produce new collections
  • 43. FOR EXPRESSION EXAMPLE def find(dir: File, predicate: String => Boolean): Seq[String] = if (!dir.exists || !dir.isDirectory) List.empty else for (f <- dir.listFiles if predicate(f.getName)) yield f.getName find(new File("C:installedmaven3bin"), _.endsWith("bat")) //ArraySeq(mvn.bat, mvnDebug.bat)
  • 44. FOR EXPRESSION EXAMPLE for ( i <- 'a' to 'c'; j <- 3 to 1 by -1 ) yield (i, j) //Vector((a,3), (a,2), (a,1), (b,3), (b,2), (b,1), (c,3), (c,2), (c,1))
  • 45. IMMUTABLECOLLECTIONS Immutability is one of the cornerstones of FP. The Scala scala.collection.immutable package offers wide variety of immutable collection data structures. If you really need mutable collections then you need to use the scala.collection.mutable package.
  • 46. val nums = 1 to 10 println(nums) //Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) val divBy3 = nums.filter(_ % 3 == 0) println(divBy3) //Vector(3, 6, 9) //nums did not change println(nums) //Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) import scala.collection.mutable val list = mutable.MutableList[Int](1, 2, 3) println(list) //MutableList(1, 2, 3) list(1) = 6 println(list) //MutableList(1, 6, 3)
  • 47. LOAN PATTERN Control abstraction pattern. The function accepts a resource and a function. The function takes care of the common repetitious work while borrowing the resource to the outer function to do something interesting with it. import java.io._ def using[A <: Closeable, B](resource: A)(op: A => B) = { try op(resource) finally resource.close() } val file = File.createTempFile("scala", "loan-pattern") using(new FileWriter(file)) { w => { w.write("Can I loan some money please?") w.flush() } } val line = using(new BufferedReader(new FileReader(file))) { r => r.readLine()
  • 48. FP+OOP=FUNCTIONAL OBJECTS FP and OOP can be used together to produce code that is concise, reusable and well structured. Let's implement a simple immutable Rational number class. First in Java and then in Scala.
  • 49. JAVAIMPLEMENTATION public final class Rational { public final int numerator; public final int denominator; public Rational(final int numerator) { this(numerator, 1); } public Rational(final int numerator, final int denominator) { if (denominator == 0) { final String msg = "Denominator must not be 0"; throw new IllegalArgumentException(msg); } this.numerator = numerator; this.denominator = denominator; } public Rational add(final Rational that) { final int n = this.numerator * that.denominator + that.numerator * this.denominator; final int d = this.denominator * that.denominator; final int gcd = ArithmeticUtils.gcd(n, d); return new Rational(n / gcd, d / gcd); }
  • 50. USINGTHEJAVARATIONAL This API looks a lot like BigInteger or BigDecimal. It's verbose. In Java can we write 1 + 1 or 3 - 2. But we can't use + and - with Rationals. In mathematical sense Rational is also a number. Wouldn't it be dreamy if we could use something like: new Rational(1).substract(new Rational(3,5)).add(new Rational(2,3)); (1 - Rational(3,5)) + Rational(2,3)
  • 51. SCALAIMPLEMENTATION final class Rational(val numerator: Int, val denominator: Int) { if (denominator == 0) throw new IllegalArgumentException("Denominator must not be 0") def +(that: Rational) = { val n = this.numerator * that.denominator + that.numerator * this.denominator val d = this.denominator * that.denominator val gcd = ArithmeticUtils.gcd(n, d) new Rational(n / gcd, d / gcd) } def -(that: Rational) = this + (-that) def unary_- = new Rational(-numerator, denominator) override def toString = numerator + "/" + denominator } object Rational { def apply(numerator: Int, denominator: Int) = new Rational(numerator, denominator) def apply(numerator: Int) =
  • 52. USINGTHESCALA RATIONAL import RationalConversions.int2SRational (1 - Rational(3,5)) + Rational(2,3) Rational(5) + 4 -Rational(8,7)
  • 53. SCALAFPGUIDELINES Don't use the var keyword. Use vals. Don't use the while keyword. Use higher order functions on collections or the for expression or recursion. Avoid the scala.collection.mutable package. Don't use null. Use Option[T] - Some(value), None. Scala is a powerful language. You can write code that looks like Java. You can write code that looks like Haskell. FP and OOP are not mutually exclusive. FP + OOP is actually a nice combination, but try to minimize state and mutability.
  • 54. ABALANCEDATTITUDEFOR SCALAPROGRAMMERS “Prefer vals, immutable objects, and methods without side effects. Reach for them first. Use vars, mutable objects, and methods with side effects when you have a specific need and justification for them.” - Programming In Scala 2nd ed
  • 55. PATTERN MATCHING
  • 56. PATTERN MATCHING FEATURES Pattern matching in Scala is basically Java switch-case on steroids. With pattern matching we can: Match values Match case classes Destructure and match collection-like structures Destructure and match arbitrary data structures using our own extractors Let's look at pattern matching in more detail using examples.
  • 57. PATTERN MATCHINGON VALUES val turn = 1 def reallyGo = false val result = turn match { case 1 => "Ready" case 2 | 3 => "Steady" //logical or case 4 if reallyGo => "Go!" //guard case _ => "Wut???" //catch all case }
  • 58. PATTERN MATCHINGON CASECLASSES-AST Abstract Syntax Tree
  • 59. PATTERN MATCHINGON CASECLASSES-AST trait AST[T] case class Expression[T](fn: (T, T) => T, left: AST[T], right: AST[T]) extends AST[T] case class Value[T](value: T) extends AST[T] def divide(x: Int, y: Int) = x / y def multiply(x: Int, y: Int) = x * y def add(x: Int, y: Int) = x + y def subtract(x: Int, y: Int) = x - y val ast = Expression(divide, Expression(add, Expression(multiply, Value(2), Value(5)), Expression(subtract, Value(3), Value(1))), Value(3))
  • 60. PATTERN MATCHINGON LISTS Most collection-like data structures in standard library have an extractor. Extractor allows us to destruct a data structure and match the inner elements. val list = List("John", "Smith", 36, List("Linda", "Smith", 32)) val msg = list match { case name :: _ :: _ :: List(spouse :: _) => name + " is married to " + spouse case _ => "Cannot make sense of the list" } println(msg) //John is married to Linda
  • 61. PATTERN MATCHINGON EXCEPTIONS try { throw new NullPointerException() } catch { case ioEx: IOException => // do something else case e @ (_ : NullPointerException | _: IllegalArgumentException) => // common logic for NPE and IAE case unexpectedEx => // catch all case } finally { //clean up }
  • 62. CUSTOMEXTRACTOR By writing our own extractor we can destruct and pattern match other data structures in a way that makes sense in our problem domain. For example some API returns URLs as Strings. We can write an extractor that allows us to destructure the URL String.
  • 63. object Url { def apply(secure: Boolean, domain: String, topLvlDomain: String) = { val protocol = if (secure) "https" else "http" protocol + "://" + domain + "." + topLvlDomain } def unapply(url: String) = { def isHttp(p: String) = p.startsWith("http") def isSecure(p: String) = p.endsWith("s") url.split("://") match { case Array(protocol, domainPart) if isHttp(protocol) => domainPart.split(".") match { case Array(domain, topLvlDomain) => Some((isSecure(protocol), domain, topLvlDomain)) case _ => None } case _ => None } } } val msg = "https://ishisystems.com" match { case Url(true, domain, _) => "Welcome to secure '%s' network".format(domain) case url => url + " is not allowed" }
  • 64. PIMPMYLIBRARY
  • 65. PIMPMYLIBRARY In real world we are mostly dealing with 3rd party APIs. Some APIs are well designed, some are not. We cannot modify them to make them easier to use with our own code. However, in Scala we can use implicit conversions to make those 3rd party APIs more richer and expressive.
  • 66. RICHDATE java.util.Date with accessories import java.util.{Calendar, Date} class RichDate(val date: Date) { def isSameDay(comparedDate: Date): Boolean = { val cal = Calendar.getInstance() def getYearAndDay(d: Date) = { cal.setTime(d) val year = cal.get(Calendar.YEAR) val day = cal.get(Calendar.DAY_OF_YEAR) (year, day) } val (y1, d1) = getYearAndDay(date) val (y2, d2) = getYearAndDay(comparedDate) y1 == y2 && d1 == d2 } } object RichDateConversions { implicit def date2RichDate(d: Date) = new RichDate(d)
  • 67. USINGRICHDATE Now we can use java.util.Date in ways the original authors did not think of. import RichDateConversions.date2RichDate val d1 = new Date val oneHour = 60 * 60 * 1000 val d2 = new Date(d1.getTime + oneHour) d1 isSameDay d2
  • 68. DON'TOVERUSEIMPLICITS Try to minimize the use of implicits. It makes the code look like it's doing something magical. Implicits are even known to confuse your IDE. If even your IDE can get confused then a developer can definitely get confused.
  • 69. SCALA&XML
  • 70. SCALA&XML Scala has good support for XML. You can: Write XML literals Write code inside XML literals Query the XML with a XPath-like API Pattern match XML
  • 71. XMLLITERALS val john = <person> <name>John</name> <age>36</age> <spouse> <person> <name>Linda</name> <age>32</age> </person> </spouse> </person>
  • 72. CODEINSIDEXMLLITERALS class Person(val name: String, val age: Int, val spouse: Option[Person]) { def toXML: Node = xml.Utility.trim( <person> <name>{name}</name> <age>{age}</age> {if (spouse.isDefined) <spouse>{spouse.get.toXML}</spouse>} </person>) override def toString: String = "name=%s, age=%s, spouse=(%s)".format(name, age, if(spouse.isDefined) spouse.get.toString else None) } val johnny = new Person("John", 36, Some(new Person("Linda", 32, None))) println(johnny.toXML) val pete = new Person("Peter", 22, None) println(pete.toXML)
  • 73. SEARCHFROMXML val john = <person> <name>John</name> <age>36</age> <spouse> <person> <name>Linda</name> <age>32</age> </person> </spouse> </person> def getSpouseName(xml: Node) = { val name = xml "spouse" "person" "name" if (name.isEmpty) None else Some(name.text) } println(getSpouseName(john)) //Some(Linda)
  • 74. PATTERN MATCHINGON XML XML pattern matching is somewhat unuseful because of the significant whitespace. def xmlToPerson(personXML: Seq[Node]): Option[Person] = personXML match { case <person><name>{name}</name><age>{age}</age><spouse>{person}</spouse></ Some(new Person(name.text, age.text.toInt, xmlToPerson(person))) case <person><name>{name}</name><age>{age}</age></person> => Some(new Person(name.text, age.text.toInt, None)) case _ => None } xmlToPerson(xml.Utility.trim(personXML))
  • 75. PARALLELISMAND CONCURRENCY
  • 76. PARALLELISMAND CONCURRENCY In Scala you can use all the tools from java.util.concurrent in addition to some extra goodies that Scala offers: Parallel collections Actor library - deprecated from core Scala and moved to Akka
  • 77. PARALLELCOLLECTIONS Most of the sequential Scala collections can be turned into a parallel counterpart with the par function. Some points about parallel collections: The main use case for parallel collections is when you need to apply a side-effect free computation to each element of the collection and the order does not matter. The computation should be non-trivial to see performance gains. Parallel collections are implemented on top of the Java fork-join framework.
  • 78. PARALLELCOLLECTIONS EXAMPLE Following example calculates factorials for each element in the collection. The numbers are quite big to make the computation more costly.
  • 79. def factorial(n: BigInt) = { @tailrec def calc(n: BigInt, acc: BigInt): BigInt = if (n == 0) acc else calc(n - 1, acc * n) calc(n, 1) } val numbers = BigInt(100000) to BigInt(100012) def measure(work: => Unit) { val start = System.currentTimeMillis work val end = System.currentTimeMillis println(end - start) } // my machine 4 cores // 12 computations // 12 / 4 => should get roughly 3x increase in performance // sequential measure(numbers.map(factorial)) // ~3 minutes // parallel measure(numbers.par.map(factorial)) // ~1 minute
  • 80. ACTORS The actor model represents a share-nothing message- passing programming model that is easier to understand for developers than shared mutable data with locks. Actor is an object that has an inbox to receive messages. Actor can process only one message at a time. Actors communicate with each other by sending immutable message objects using the ! method.
  • 81. ACTOR BENEFITS No shared data No locks & synchronization Asynchronous message passing Actors can share a thread pool Combine actors with java.util.concurrent classes. For example, multiple actors can share a ConcurrentHashMap Akka offers many other benefits such as support for distributed actors and fault tolerance, but that is another subject
  • 82. ACTOR USECASES Simulating workflows Distributed computing (with scatter-gather actor design pattern) Systems that need event driven architecture
  • 83. EXAMPLE Sprint workflow
  • 84. WORKFLOW
  • 85. TOPOLOGY
  • 86. MESSAGES Let's define the messages as case classes so they can be pattern matched by actors. case class StartSprint() case class Develop(story: String) case class FixBug(story: String) case class DevelopmentDone(story: String) case class Test(story: String) case class TestingDone(story: String, bug: Boolean) case class Release(story: String) case class NewFeature(story: String)
  • 87. DEVELOPER ACTOR class Developer(val name: String) extends Actor { def receive = { case Develop(story) => develop(story) case FixBug(story) => fixBug(story) } def develop(story: String) { println(name + " develops " + story) tracker ! DevelopmentDone(story) } def fixBug(story: String) { println(name + " fixes bug in " + story) tracker ! DevelopmentDone(story) } }
  • 88. TESTER ACTOR class Tester(val name: String) extends Actor { def receive = { case Test(story) => test(story) } def test(story: String) { val foundBug = Random.nextBoolean() val log = if (foundBug) name + " found a bug in " + story else name + " verified that " + story + " works" println(log) tracker ! TestingDone(story, foundBug) } }
  • 89. RELEASEMANAGER ACTOR class RM extends Actor { def receive = { case Release(story) => { println("RM deployed " + story + " to production") client ! NewFeature(story) } } }
  • 90. CLIENTACTOR class Client extends Actor { def receive = { case NewFeature(feature) => { println("Client tries out " + feature + " and is satisfied with it") } } }
  • 91. TRACKER ACTOR As seen from the diagrams the Tracker actor coordinates the work between developers, testers and the release manager. class Tracker(stories: Seq[String]) extends Actor { /** * Map of Story -> (Dev, Tester) * Each story gets assigned a developer and a tester. */ val sprintPlan = { val numberOfStories = stories.size def repeat[T](resources: Seq[T]) = Stream.continually(resources).flatten.take(numberOfStories) val assignments = (stories, repeat(developers), repeat(testers)).zipped.toList assignments.map { case (story, dev, tester) => (story, (dev, tester)) }.toMap }
  • 92. APPCONTEXT Container that creates the actor system. object AppContext { println("Setting up AppContext...") val actorContext = ActorSystem("SprintAppActors") val tracker = { val stories = for (i <- 1 to 10) yield "Feature-" + i toActor(new Tracker(stories), "tracker") } val developers = toActors(List("Rasmeet", "Viral", "Dhruv"), (name: String) => new Developer(name), "developers") val testers = toActors(List("Dilip", "Shaishav"), (name: String) => new Tester(name), "testers") val val releaseManager = toActor(new RM, "RM") val client = toActor(new Client, "Client") def toActors[T](xs: Seq[T], fn: T => Actor, groupName: String) = xs.zipWithIndex.map { case (x, i) => toActor(fn(x), groupName + "-" + i)
  • 93. LAUNCHTHEAPP import AppContext._ object Simulation { def main(args: Array[String]) { tracker ! StartSprint } }
  • 94. SAMPLEOUTPUT Setting up AppContext... Starting sprint Rasmeet develops Feature-10 Viral develops Feature-5 Dhruv develops Feature-6 Dhruv develops Feature-9 Dhruv develops Feature-3 Rasmeet develops Feature-1 Viral develops Feature-2 Rasmeet develops Feature-7 Viral develops Feature-8 Rasmeet develops Feature-4 Shaishav found a bug in Feature-5 Dilip verified that Feature-6 works Shaishav verified that Feature-2 works Shaishav verified that Feature-8 works Dilip found a bug in Feature-10 Dilip verified that Feature-9 works Dilip found a bug in Feature-3 Dilip found a bug in Feature-1 Dilip verified that Feature-7 works Dilip found a bug in Feature-4 Viral fixes bug in Feature-5 RM deployed Feature-6 to production Rasmeet fixes bug in Feature-10 Rasmeet fixes bug in Feature-1
  • 95. SCALAIN REALWORLD
  • 96. OBSERVATIONS IDE. Intellij is currently hands down the best IDE for Scala. Eclipse Scala plugin is coming along but still has too many bugs and quirks to make it worthwile. Also, Eclipse in general has been getting slower with each successive release.
  • 97. OBSERVATIONS Code is really concise. Passing functions to other functions is awesome. I really do feel encouraged to write immutable classes and avoid changing state as Scala makes it easy to write without ceremony. The downside is that developers can write too concise code that becomes hard to read. Best advice is to try to keep the code simple even if it means writing a bit more code.
  • 98. OBSERVATIONS It's easy to interop with existing Java code. However, it's not so easy to call some of the Scala code from Java.
  • 99. OBSERVATIONS Scala method definitions can be hard to understand. // Wut me supposed to do wit it??? scan[B >: A, That](z: B)(op: (B, B) ? B)(implicit cbf: CanBuildFrom[List[A], // Huh? :+[B >: A, That](elem: B)(implicit bf: CanBuildFrom[Repr, B, That]): That
  • 100. OBSERVATIONS When the team uses Scala code reviews become very important. People with different programming backgrounds will use Scala differently. Haskell guys will write it as if it were Haskell. Java guys will write it as if it were Java. In general it's better to lean towards FP principles as it tends to produce code that is more reusable and is easier to understand in isolation.
  • 101. OBSERVATIONS Scala really shines when it comes to mixing OOP with FP, but it's not a good language to learn FP. You will benefit a lot if you get your hands dirty with Haskell or Clojure and then come back to Scala with a new perspective.
  • 102. SCALAADOPTION STRATEGY Decide as a team whether you want to use Scala. This is key. Start by writing test cases in Scala. You will be only using basic vanilla Scala, but at least it will give you confidence that you can effectively use Scala together with the existing code base. If you're introducing a new backend component or breaking up an existing monolithic component give Scala a chance.
  • 103. LEARNINGMATERIALS Free online Scala course on Coursera Programming In Scala 2nd ed Scala in Depth Functional Programming in Scala Twitter's Scala School Official documentation Akka documentation Scala Days 2013 videos InfoQ Scala presentations 99 Scala problems
  • 104. THANKYOU