scala for java developers (silicon valley code camp 13)
DESCRIPTION
My presentation at Silicon Valley Code Camp 13 (http://www.siliconvalley-codecamp.com/Session/2013/scala-for-java-developers). The target audience is Java developers who wants to get started with Scala.TRANSCRIPT
Scala for Java Developers
Ramnivas Laddad @ramnivas
@ramnivas
• Author of books and articles – AspectJ in Action (1st and 2nd edition)
• Spring framework, Cloud Foundry • Main interests
– Cloud computing – Aspect-oriented programming – Scala and functional programming
• Speaker at many professional conferences – JavaOne, JavaPolis, SpringOne, Software Development, No Fluff Just Stuff,
EclipseCon, O’Reilly OSCON etc.
• Active involvement in AspectJ, Spring, and Cloud Foundry since their early form
What is Scala
3
“a general purpose programming language designed to express common programming patterns in a concise, elegant, and type-safe way. It smoothly integrates features of object-oriented and functional languages, enabling Java and other programmers to be more productive.”
http://www.scala-lang.org
Object-oriented
• Everything is an object – No “primitives”
• Classes – Same as Java, but concise
• Traits – Interfaces done right
• Singletons – Language-level concept
4
Statically typed
• Rich type system (by Java’s standard) – Higher-kinded types – Implicit conversions – Type evidence
• Expressive type system • Inferred types
5
Functional Programming
• Functions as values – May be
• Saved • Passed to other functions (higher-order functions) No need to write ugly anonymous classes
– Advanced pattern matching – Expressions return a value
• if/else, try/catch, match, …
• Promotes immutability – But doesn’t force it
6
Java Interoperability
• Compiles to Java byte code – Jars, wars, … – No special operational change
• Scala calling Java, Java calling Scala code is fine • Whole Java eco-system at your service
• You can write apps using Scala and Spring, today
7
Hello World: Scripting Style
$ scala hello-script.scalaHello World
println("Hello World")
No compilation
Hello World: Porting of Java Code
$ scalac hello-java.scala$ scala example.MainHello World
// hello-java.scalapackage exampleobject Main { def main(args: Array[String]) {
println("Hello World") }}
‘static’
Inferred semicolons
Hello World: Using the App trait
$ scalac hello-app.scala$ scala example.MainHello World
// hello-app.scalapackage exampleobject Main extends App { println("Hello World") }
Simple Class
class Person
val p = new Person
Type Inferred
Default access: public
No curly braces needed (but allowed)
Simple Class
class Person
val p: Person = new PersonExplicit type specification
Class with constructor
class Person(firstName: String, lastName: String)
val p = new Person("Ramnivas", "Laddad") println(p.firstName) // Error
Primary constructor
Fields – accessible in class body
Class with “getters”
class Person(val firstName: String, val lastName: String)
val p = new Person("Ramnivas", "Laddad") println(p.firstName)
Value (Java ‘final’)
Class with “getters” and “setters”
class Person(var firstName: String, var lastName: String)
val p = new Person("Ramnivas", "Laddad")println(p.firstName)p.firstName = "Ramnivas2”
Variable (Java non-final)
Extending a class
class Student(firstName: String, lastName: String, val grade: Int) extends Person(firstName, lastName)
val s = new Student("Ramnivas", "Laddad", 1)println(s.firstName)println(s.grade)
Defining methods
class Person(val firstName: String, val lastName: String) { def name = firstName + " " + lastName override def toString = name }
val p = new Person("Ramnivas", "Laddad")println(p.name) // Ramnivas Laddadprintln(p) // Ramnivas Laddad
Not optional
Uniform access principle
class Person(val firstName: String, val lastName: String) { val name = firstName + " " + lastName override def toString = name }
val p = new Person("Ramnivas", "Laddad")println(p.name) // Ramnivas Laddadprintln(p) // Ramnivas Laddad
Names in Scala
• Class, method, field names can contain non alpha-numeric characters – :: – ::: – ~> – f@#:
• Valuable if used judiciously – DSLs
19
More about methods and fields
• Declaring abstract methods and fields – Just don’t provide definition def learn(subject: String) val knowledge
• Classes with abstract method must be declared abstract – Just as in Java
• Methods can be defined inside methods • Methods may be marked @tailrec to check for tail-
recursiveness
20
Finer access control levels
• Default access level: public • Protected: protected
– Same as Java • Private:
– private – private[this] Access only from this instance – private[package-name] Access from package and its
subpackages
21
Traits: Interfaces done right
22
trait PartyGoer { val age: Int val yearsUntilLegalDrinking = if (age >= 21) 0 else 21-age}
class Student(firstName: String, lastName: String, val age: Int, val grade: Int) extends Person(firstName, lastName) with PartyGoer
val s = new Student("a", "b", 17, 12)s.yearsUntilLegalDrinking // 4
Collections
val people = List("John", "Jacob", "Mike")
val firstPerson = people(0)println(firstPerson) // John
Collections
val people = Array("John", "Jacob", "Mike")
val firstPerson = people(0)println(firstPerson) // John
Working with collections: for comprehension
25
for (person <- people) { println(person)}
Working with collections: for comprehension
26
for (person <- people if person startsWith "J") { println("""Lucky one to start name in "J" """ + person)}// You are lucky one to start name in "J" John// You are lucky one to start name in "J" Jacob
Working with collections: for comprehension
27
for (person <- people if person startsWith "J") { println(s"""Lucky one to start name in "J" $person""")}// You are lucky one to start name in "J" John// You are lucky one to start name in "J" Jacob
Working with collections: filter
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
val firstGraders = students.filter( s => s.grade == 1)println(firstGraders) // List(first1 last1, first2 last2)
Working with collections: filter
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
val inFirstGrade: Student => Boolean = s => s.grade == 1 val firstGraders = students.filter(inFirstGrade)// List(first1 last1, first2 last2)
Working with collections: using _
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
val firstGraders = students.filter(_.grade == 1)println(firstGraders) // List(first1 last1, first2 last2)
Working with collections: passing method as function
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
def inFirstGrade(s: Student) : Boolean = s.grade == 1 val firstGraders = students.filter(inFirstGrade)// List(first1 last1, first2 last2)
Working with collections: partition
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
val (elementarySchoolers, middleSchoolers) = students.partition(_.grade < 6)println(elementarySchoolers)println(middleSchoolers)//List(first1 last1, first2 last2, first3 last3)//List(first4 last4)
Tuple
Working with collections: transforming
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
val rollCall = students.map(_.firstName)println(rollCall)// List(first1, first2, first3, first4)
Map
// student1, student2, student3, student4val studentSchools = Map(student1 -> "Miller", student2 -> "Lawson", student3 -> "Lawson", student4 -> "Miller")
println(studentSchools(student1)) // Miller
More collections
• Set • IndexedSeq • Vector (good one!) • Range
– 1 to 100 – 1 until 100 – 2 until 100 by 2
• …
• Mutable versions • Parallel versions
35
Putting it together: Quicksort
def quicksort[T](input: Traversable[T]) (ordering: Ordering[T]) : Traversable[T] = if (input.isEmpty) { input } else { val (low, high) = input.tail.partition(ordering.lt(_, input.head)) quicksort(low)(ordering) ++ List(input.head) ++ quicksort(high)(ordering) }
println(quicksort(List(1, 3, 4, 5, 1))(Ordering.Int))
Putting it together: Quicksort
def quicksort[T](input: Traversable[T]) (implicit ordering: Ordering[T]) : Traversable[T] = if (input.isEmpty) { input } else { val (low, high) = input.tail.partition(ordering.lt(_, input.head)) quicksort(low) ++ List(input.head) ++ quicksort(high) }
println(quicksort(List(1, 3, 4, 5, 1)))
Pattern matching: Basics
val a:Any = "foo" a match { case str: String => println("A string: " + str) case i: Int => println("An int: " + i) case _ => println("Something else")}
Pattern matching: with collections
val l = List("a", "b", "c") l match { case Nil => println("Empty!") case head :: Nil => println("Only one item " + head) case head :: tail => println("Item " + head + " followed by " + tail) }
Quicksort with pattern matching
def quicksort[T](input: Traversable[T]) (implicit ordering: Ordering[T]) : Traversable[T] = input match { case head :: tail => val (low, high) = tail.partition(ordering.lt(_, head)) quicksort(low) ++ List(head) ++ quicksort(high) case _ => input }
println(quicksort(List(1, 3, 4, 5, 1)))
Destutter using Pattern matching
41
def destutter[A](lst: List[A]): List[A] = lst match { case h1 :: h2 :: tail if (h1 == h2) => destutter(h2 :: tail) case h1 :: h2 :: tail => h1 :: destutter(h2 :: tail) case _ => lst}
// destutter(List(1,1,1,1,1,1)) => List(1)// destutter(List(1,1,4,3,3,2)) => List(1,4,3,2)// destutter(List())=> List()
Case classes
• Useful in pattern matching – “case”
• Offer many useful common methods – equals() – hashCode() – toString – copy()
42
Case classes
43
case class Human(name: String)case class SuperHero(name: String, power: String)
val characters = List(Human("Programmer"), SuperHero("Customer", "money"), SuperHero("QA", "testing"))
Case classes and pattern matching
44
val actions = for (character <- characters) yield character match { case Human(name) => name + " needs to be saved" case SuperHero(name, power) => name + " will save using " + power }actions.foreach(println)
Pattern matching and extracting just enough
45
val actions = for (character <- characters) yield character match { case Human(name) => name + " needs to be saved" case SuperHero(_, power) => "Could be saved using " + power }actions.foreach(println)// Programmer needs to be saved// Could be saved using money// Could be saved using testing
Regular expressions
46
val text = "Ramnivas Laddad" val Name = """(\w+)\s+(\w+)""".rval person = text match { case Name(first, last) => Some(new Person(first, last)) case _ => None}println(person) // Some(Ramnivas Laddad)
Options
47
val texts = List("Ramnivas Laddad", "foo", "Martin Odersky")val peopleOptions = texts.map { _ match { case Name(first, last) => Some(new Person(first, last)) case _ => None }}println(peopleOptions)// List(Some(Ramnivas Laddad), None, Some(Martin Odersky))
Options: flattening
48
val texts = List("Ramnivas Laddad", "foo", "Martin Odersky")val peopleOptions = texts.map { _ match { case Name(first, last) => Some(new Person(first, last)) case _ => None }}println(peopleOptions.flatten)// List(Ramnivas Laddad, Martin Odersky)
Options: flatMap
49
val texts = List("Ramnivas Laddad", "foo", "Martin Odersky")val people = texts.flatMap { _ match { case Name(first, last) => Some(new Person(first, last)) case _ => None }}println(people)// List(Ramnivas Laddad, Martin Odersky)
Higher order functions
def process() : Unit = { retry(5) { ... }}
def retry[T](maxRetry: Int)(thunk: => T) = { def loop(thunk: => T, attempt: Int): T = { try { thunk } catch { case ex if (attempt < maxRetry) => loop(thunk, attempt + 1) } } loop(thunk, 0)}
Thunk
Caching using Scala
def getQuoteGraph(stock: Stock, days: Int) : Array[Byte] = { cached("chart", stock.ticker + ":" + days) {! ... Expensive calculation !! }}
Caching higher-order function
abstract class Caching(val cacheManager: CacheManager) { def cached[T](region: String, key: Any) (thunk: => T): T = { val cache = ... if (cache.containsKey(key)) { cache.get(key).asInstanceOf[T] } else { val thunkVal: T = thunk cache.put(key, thunkVal) thunkVal } }}
Transaction management
def findOrder(orderId: Long) : Order = { transactional(readOnly=true) { //... }} def updateOrder(order: Order) { transactional() { //... }}
53
Transaction management function def transactional[T](propgation: Propagation = Propagation.REQUIRED, isolation: Isolation = Isolation.DEFAULT, readOnly: Boolean = false, timeout: Int =TransactionDefinition.TIMEOUT_DEFAULT, rollbackFor: List[Throwable] = List(), noRollbackFor: List[Throwable] = List()) (thunk: => T) : T
Transaction management implementation abstract class TransactionManagement(val txManager: PlatformTransactionManager) { def transactional[T](...)(thunk: => T) : T = { val txAttribute = new TransactionAttributeWithRollbackRules(...) val status = txManager.getTransaction(txAttribute) try { val ret = thunk txManager.commit(status) ret } catch { case ex => { if (txAttribute.rollbackOn(ex)) { txManager.rollback(status) } else { txManager.commit(status) } throw ex } } }}
There is more… a lot more • Methods/functions
– Default parameters – Named parameters – Curried parameters – Partial, partially-applied
functions • Type system
– Higher-kinded types – Bounded types – Implicit type conversion – Type parameter
evidence – Type aliasing
• Lazy values • Partial imports • Actors • Extractors • Scala ecosystem • Combinator/parser • Continuations • Compiler plugin • …
56
Learning Scala
• Read a Scala book – Get the whole picture
• May feel complex at first – Java-style Scala may serve best during initial exploration – Over time you will appreciate its simplicity – Will affect your non-Scala programming deeply
57
Be ready to be humbled Be ready to have fun!
Scala for Java Developers
Ramnivas Laddad @ramnivas