Welcome !
?
Intro into Scala
For Java developers
Joost den Boer
Contractor, Senior developer, Scala enthusiast
@diversit
www.diversit.eu
THIS IS NOT A SCALA COURSE !!
Topics
- What is Scala
- Why Scala
- Some Basics
- Features
- Frameworks
- Getting started
- Resources
What is Scala?
- Functional and OO language
- Runs on JVM
- Started in 2001
- By Martin Oderski (worked on Java Generics and javac)
- @EPFL in Lausanne
- European funds in 2011
- Typesafe founded in 2011
- Typesafe stack: Scala, Play, Akka, Activator
Why Scala?
- It's Java 12+ available NOW
- Binary compatible with Java
- Good interoperability
- Reuse of existing code/library
- Loads of advanced features Java lacks
- Java evolution too slow
- Less code, less bugs
- Easier to read
- Concurrency by design
- Better suited for current and future hardware
- Functional programming makes concurrency simpler
- Immutability
- Methods without side effects, repeatable
- Complex functionality by composing multiple simple functions
- Higher level of abstractions, more reuse of ideas
- Superior Generics
- Build custom DSL's
More why Scala?
Some basics
- Everything is an object (extends Any)
- Function is a first class object
- As are singletons !
- Every operation is a method
- Every statement is an expression
- Methods in methods
- 'return' keyword is optional
- call-by-name
- Unchecked exceptions
- Supports checked exceptions for Java compatibility
Features:
- String interpolation
- Traits
- Concurrency, Reactive programming
- Native XML support
- Box types: Option
- Powerfull Collection API
- Case classes
- Pattern matching
- Currying
- Implicits
- Lambdas and closures
Features: String interpolation
val height = 1.9d
val name = "James"
println(f"$name%s is $height%2.2f meters tall") // James is 1.90
val name = "Piet"
s"Hello $name"
s"Sum is ${3 + 3}"
s"User name: ${user.name}"
"a\nb"
raw"a\nb"
"""a\nb"""
Extendable: create your own.
E.g. json"{ name:Piet }"
Feature: Traits
- Like interfaces with vars and methods
Only compatible with Java when Trait only contains method definitions.
(might change with Java 8's default methods)
trait SomeService {
val value = 1
abstract def getValue: Int
def toString = s"Value is ${value}"
}
class MySomeService extends SomeService
val mySomeService = new SomeService {}
- Multiple inheritance (sort of, linear path)
trait Logging
trait SomeService extends Logging
trait OtherService extends Logging
class MySomeService extends SomeService with OtherService
Feature: Traits
- Build-in DI support (Cake pattern)
- override define values with other implementation
- Dynamic @ runtime
val person = new Employee extends Management with HRM with CFO
- Self typing
trait UserRepository {
def find(id: Int): User
}
trait UserService {
self: UserRepository =>
def findUser(id: Int) = find(id)
}
Feature: concurrency,
reactive programming
- Immutability by default (shared state is hard)
- Composable futures
- Promises
- .par()
- Actors (Akka)
Feature: native xml support
// Create XML
val phoneBook =
<phonebook>
<descr>This is the <b>phonebook</b></descr>
<entry>
<name>Burak</name>
<phone where="work">+41 21 693 68 67</phone>
<phone where="mobile">+41 79 602 23 23</phone>
</entry>
</phonebook>
// Manupilate XML
val updatedPhonebook = phonebook match {
case <phonebook>{ ch @ _* }</phonebook> =>
<phonebook>{ ch }<entry> ... </entry></phonebook>
}
// XPath-like
phonebook \\ "phone" map (_.text)
// returns List[String]
Feature: Box types
Option's done right
No more 'null's and NPE's !
sealed abstract class Option[T]
final case class Some[T] extends Option[T]
object None extends Option[Nothing]
val i = Some(1) // Option[Int]
val user = Some(user) // Option[User]
val none: Option[User] = None // Option[User]
var noneWrong = None // None.type
noneWrong = Some(1) // Error: type mismatch;
// found: Some[Int], required None.type
User u = someMethodReturningNUll();
u.getName(); // --> NPE
// Alternative
if (u != null) u.getName();
// Or
try {
u.getName();
} catch (NullPointerException e) { .. }
In Java:
In Scala:
val user: Option[User] = someMethodReturningAUserOption()
u.map(_.name) // Option[String]
u.map(_.name).filter(_ contains "Piet") // Option[String]
u.map(_.name).filter(_ contains "Piet").getOrElse("Niet Piet") // String
u.map(_.name).filter(_ contains "Piet").orElse(Some("Niet Piet")) // Option[String]
Feature: Options (2)
Feature: Options (3)
For comprehension
for {
user <- someMethodReturningAUserOption()
name <- Some(user.name) if (user.name == "Piet")
} yield name // Option[String]
Pattern matching
someMethodReturningAUserOption match {
case Some(user) if (user.name == "Piet") => ..
case Some(User("Piet")) => .. // werkt ook
case Some(user) => ..
case None => ..
case _ => .. // catches all others possibilities
}
Feature: Options (4)
Other box types:
- Try ( Success or Failure )
- Either ( Left or Right )
Try { u.name } // Try[String]: Success(name) or Failure(exception)
Alternative for Option for dealing with missing values
Left = ok, Right = wrong
Can be used to gather multiple validation errors (Right: List[String])
Feature: Powerfull Collection API
In Java:
List<User> people = ...
List<String> adults = new ArrayList<String>();
for(User user : people) {
if(user.getAge() > 18) {
adults.add(user.getName());
}
}
In Scala:
List[User] people = ...
val adults = people.filter(_.age > 18).map(_.name)
List<User> people = ...
List<String> adults = people.stream()
.filter(p -> p.getAge() > 18)
.map(User::getName())
.collect(Collectors.toList());
In Java8:
Feature: Powerfull Collection API (2)
map, flatMap, filter, filterNot, find, forall, foreach, fold, mkString, drop, take, head, last, init, tail, exists, contains,
partition, slice, zip, reduce, reverse, sliding,
sum, max, min, product,
union, diff, intersect, ...
Some other functions:
class User {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public void setName(String name) {
this.name = name;
}
public Integer getAge() { return age; }
public void setAge(Integer age) {
this.age = age;
}
public boolean equals(Object that) { ... }
public int hashCode() { ... }
public String toString() { ... }
}
Java:
Scala:
case class User(name: String, age: Int)
This includes:
- Proper hashCode() and equals()
- toString()
-
companion object
- User("Piet", 33)
Feature: Case Classes
Feature: Pattern matching
- matching collection types
- maching case classes
- vars from tuples
- regex
Collection type matching
val aList = List(1,2,3)
// Example matching Lists
aList match {
case 1 :: twee :: rest => println(twee)
case head :: tail => println(tail)
case Nil => println("Nothing")
}
Feature: Pattern matching
Case class matching
case class User(name: String, age: Int)
val piet = User("Piet", 80)
piet match {
case u @ User(name, _) => ..
case _ => ..
}
val userList = List(User("Piet", 80), User("Ruud", 54), User("Kees", 23))
userList map {
case User(name, age) if age > 18 => name
case _ => "too young"
}
val names = userList map {
case User(name, age) if age > 18 => Some(name)
case _ => None
} // List[Option[String]]
names.flatten // List[String] without the None's
Feature: Pattern matching
Matching Tuples
val t = (1, "abc", List(1,2,3))
val (i, s, l2) = t // i=1, s="abc", l2=List(1,2,3)
t match {
case (_: Int, _, _) => println("Int")
}
Regex matching
val regex = """^INFO: (.*)$""".r
val regex(msg1) = "INFO: testing" // msg1 = "testing"
val msg2 = "INFO: testing" match {
case regex(msg) => msg
} // msg2 = "testing"
Feature: currying
Introducted by Moses Schönfinkel.
Later developed by Haskell Curry.
"currying is the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument (partial application)"
// uncurried
def sum(x: Int, y: Int): Int = x + y
sum(1,4) // 5
Feature: currying
// curried
def sum: Int => Int => Int = x => y => x + y
val addOne = sum(1) // Int => Int = <function1>
addOne(2) // 3
sum(1)(4)
- Multiple argument lists.
- Partially applied funtions
To curry and uncurry
// To curry
def sum1(x: Int, y: Int) = x + y // sum1: (x: Int, y: Int)Int -> NOT a Function!
val fSum1 = sum1 _ // (Int, Int) => Int = <function2> -> Function taking 2 args
val cSum1 = fSum1.curried // res48: Int => (Int => Int) = <function1>
Function.uncurried(cSum1) // (Int, Int) => Int = <function2>
Partial functions: PartialFunction[A-, B+]
// Actor's receive method
def receive: Receive = {
case SomeMessage(content) => // do something with content
}
'case' matching to create partial function
val list = List(1,2,3,4,5,6)
val result = list.collect { case x if x > 3 => x }
// What is result?
result == List(4,5,6)
Feature: implicits
Java implicits:
- autoboxing
- default constructor
- calling super() in constructor
To automatically determine or refer to a value
Scala implicits:
- Automatically determine values for arguments.
- Automatically find converters
Implicits are looked up in specific order: Scopes
Feature: implicits
implicit val one: Int = 10
def addOne(x: Int)(implicit y: Int) = x + y // (x: Int)(implicit y: Int)Int
addOne(3) // 4
Bad example!
Implicits look at types, not at value names.
So, use types when using implicit values.
type One = Int // wrong: will still match all implicit Int's
type IntCheck = Int => Boolean // already more specific
implicit val biggerThanTwo: IntCheck = x => x > 2
def check(x: Int)(implicit checker:IntCheck) = checker(x)
class Adder(value: Int)
implicit val one = Adder(1)
def addOne(x: Int)(implicit y:Adder) = x + y
Feature: implicits
Implicit conversions: Very powerfull !!
Also here: use Types
class Adder(val x: Int)
implicit def adderToString(a: Adder) = s"Adder value: ${a.x}"
def someMethod(s: String) = println(s)
someMethod(new Adder(10)) // implicit conversion to String!
Extend existing classes with new functionality!
(like Caterogies in Objective C)
implicit class IsTodayDate(val date: java.util.Date) extends AnyVal {
def isToday: Boolean = new Date().equals(date)
}
new java.util.Date().isToday
Implicit Value classes are NOT instanciated!
Feature: implicits
Convert between Java <-> Scala types:
Best practive: Use JavaConverters, not JavaConversions !
import scala.collection.JavaConverters._
val sl = new scala.collection.mutable.ListBuffer[Int]
val jl : java.util.List[Int] = sl.asJava
val sl2 : scala.collection.mutable.Buffer[Int] = jl.asScala
assert(sl eq sl2)
Lambdas and Closures
Lamda expression: a function defined, and possible called, without being bound to an identifier.
Closure: a function which accesses non-local variables
- Java <8: CallBack objects
- Java 8 : Lambda expressions
- Scala : Fully functional, Function is an object
// Assume Person class with name and age.
List<Person> persons = new ArrayList() {{
add(new Person("Piet", 40));
add(new Person("Kees", 18));
add(new Person("Eva", 18));
}};
Map<Integer, List<String>> personsByAge = persons.stream()
.filter(p -> p.age > 16)
.collect(
Collectors.groupingBy(
p -> p.getAge(),
Collectors.mapping(p -> p.getName(), Collectors.toList())
)
);
System.out.println(personsByAge);
// Assume Person class with name and age.
List<Person> persons = new ArrayList() {{
add(new Person("Piet", 40));
add(new Person("Kees", 18));
add(new Person("Eva", 18));
}};
Map<Integer, List<String>> personsByAge = persons.stream()
.filter(personsOver16())
.collect(
Collectors.groupingBy(
getPersonAge(),
personNamesList()
)
);
System.out.println(personsByAge);
static Predicate<Person> personsOver16() {
return p -> p.getAge() > 16;
}
static Function<Person, Integer> getPersonAge() {
return p -> p.getAge();
}
static Function<Person, String> getPersonName() {
return p -> p.getName();
}
static Collector<? super Person, ?, List<String>> personNamesList() {
return Collectors.mapping(getPersonName(), Collectors.toList());
}
// Scala oplossing
case class Person(val name: String, val age: Int)
val persons = List(Person("Piet", 40), Person("Kees", 18), Person("Eva", 18))
def personsNameList: List[Person] => List[String] = ps => ps.map(_.name)
val personsByAge = persons.groupBy(_.age).mapValues(personsNameList)
println(personsByAge)
How to do this in Java?
def sum: Int => Int => Int = x => y => x + y
sum(1)(3) // 4
static Function<Integer,Function<Integer, Integer>> fSum() {
return x -> y -> x + y;
}
fSum().apply(1).apply(2);
Advanced features
Macros
- Extend the language yourself
- AST manipulations
- Experimental
Reflection
- Type info at runtime
- new in 2.10
- Often used with implicits
Monads, Monoids, Functors, Applicatives
Advanced features
Advanced Generics
- E.g. only support sum() method on list of values
- E.g. Builder pattern, enforce order of methods
Covariant, Contravarant, Invariant
Covariant: convert from wider to narrower type.
Contravariant: convert from type to wider type.
Invariant: Not able to convert.
Frameworks
- Testing: ScalaTest, Specs2
- Web: Play, Lift
- Rest: Spray, Scalatra
- Reactive: Akka
- Tools: Scalaz, Shapeless
Getting started
IntelliJ, Eclipse
SBT, Maven Plugin
Download from Typesafe, Homebrew (Mac)
Mixing with Java: Scala compile first (default)
- Precompiles public Java classes api
- Then compiles Scala classes
- Then compiles Java classes
Testing excelent way to get started!
Resources
- Typesafe.com
- Several learning resources
- Typesafe Activator
- ScalaDocs
- Loads of online presentations and slides
- Parlays.com (Devoxx and ScalaDays videos)
- Twitter Scalaschool
- CakeSolutions: Week-in-Scala blog
- Coursera course
- 'Functional Programming in Scala'
- Start september 15th !
- 'Reactive Programming'
- 'Functional Programming in Scala'
-
Danial Westheide's 'Neophyte's Guide to Scala'
- http://danielwestheide.com/scala/neophytes.html
?
Scala intro for Java developers
By Joost den Boer
Scala intro for Java developers
An introduction into the cool feature of Scala you're missing out on when using Java.
- 1,613