An Introduction to Scala – Erik LaBianca – Hung Lin



An Introduction to Scala – Erik LaBianca – Hung Lin

1 1


scala-intro

An introduction to scala

On Github easel / scala-intro

An Introduction to Scala

Erik LaBianca

Hung Lin

July 20, 2016

@easel @hunglin @easel erik.labianca@gmail.com https://www.linkedin.com/in/eriklabianca https://erik.labianca.org/scala-introhttps://github.com/easel/scala-intro

Overview

Variables Types Control Flow Functions Structures Syntax Implicits Collections

Variables

Immutable values don't change. Use them whenever possible

scala> val x = "a string"
x: String = a string

scala> x = "another string"
<console>:13: error: reassignment to val
       x = "another string"
         ^

scala> x
res0: String = a string

Mutable variables can change.

scala> var x = "a mutable string"
x: String = a mutable string

scala> x = "another string"
x: String = another string

scala> x
res1: String = another string

Lazy Variables

Variables can be lazy. Not evaluated until used.

scala> val x = {
     |   println("defining x")
     |   1
     | }
defining x
x: Int = 1

scala> lazy val lazyX = {
     |   println("defining lazyX")
     |   1
     | }
lazyX: Int = <lazy>

scala> x
res2: Int = 1

scala> x
res3: Int = 1

scala> lazyX
defining lazyX
res4: Int = 1

scala> lazyX
res5: Int = 1

Scope Rules

Closures

Types

Can be Inferred

scala> val x = 1
x: Int = 1

Can be Ascribed

scala> val x: Long = 2
x: Long = 2

scala> val y = 2:Long
y: Long = 2

Can be Aliased

scala> type MyLong = Long
defined type alias MyLong

scala> val x: MyLong = 2
x: MyLong = 2

Type Hierarchy

Primitive Types

scala> val aChar = 'A'
aChar: Char = A

scala> val aString = "A String"
aString: String = A String

scala> val aByte = 1:Byte
aByte: Byte = 1

scala> val aShort = 1:Short
aShort: Short = 1

scala> val anInt = 1
anInt: Int = 1

scala> val aLong = 1L
aLong: Long = 1

scala> val aFloat = 1.0F
aFloat: Float = 1.0

scala> val aDouble = 1.0D
aDouble: Double = 1.0

scala> val aBoolean = false
aBoolean: Boolean = false

scala> val aUnit = ()    
aUnit: Unit = ()

Packages

Import

Type Parameters

Abstract Types

Higher-Kinded Types

Control Flow

  • Everything is an expression.
  • Sometimes that expression returns a Unit

If / Then Conditionals

scala> val x = if(1 == 1) "true" else "false"
x: String = true

While / Do Loops

scala> var i = 0
i: Int = 0

scala> val x = while(i < 2) { println(i); i += 1 }
0
1
x: Unit = ()

scala> do { println(i); i += 1 } while (i < 4)
2
3

Pattern Matching

scala> val x = 1 match {
     |   case 1 => "true"
     |   case _ => "false"
     | }
x: String = true

For comprehensions

scala> for (i <- 0 to 1) println(i)
0
1

scala> for (i <- 0 until 1) println(i)
0

scala> for (i <- Seq("aString", "bString")) println(i)
aString
bString

scala> for (a <- Option.empty[String]; b <- Option("bString")) println(a+b) 

scala> for (a <- Option("aString"); b <- Option("bString")) println(a+b)
aStringbString

//## Exception Handling

Pure Functions

Takes parameters. Produce a result.

scala> val input = "a test string"
input: String = a test string

scala> def myPureFunc(myParam: String): Int = myParam.length
myPureFunc: (myParam: String)Int

scala> myPureFunc(input)
res0: Int = 13

scala> myPureFunc(input)
res1: Int = 13

Impure Functions

Takes parameters. Does something else (side effects). Produces a result.

scala> val input = "a test string"
input: String = a test string

scala> var x = 0
x: Int = 0

scala> def mySideEffectingFunc(myParam: String): Int = {
     |   x += myParam.length
     |   x
     | }
mySideEffectingFunc: (myParam: String)Int

scala> mySideEffectingFunc(input)
res2: Int = 13

scala> mySideEffectingFunc(input)
res3: Int = 26

Procedures

Takes Parameters. Does something else (side effects). No result.

scala> def myProc(myParam: String): Unit = println(s"hello world - $myParam")
myProc: (myParam: String)Unit

scala> myProc("it's a beautiful day")
hello world - it's a beautiful day

Parameters can be passed by name

Takes parameters. Wraps them in a function. Re-evaluate's whenever used.

scala> def myParamFunc = { 
     |   println("getting parameter"); 
     |   "a string" 
     | }
myParamFunc: String

scala> def myFunc(myParam: String): String = { 
     |   println("executing myFunc"); 
     |   myParam 
     | }
myFunc: (myParam: String)String

scala> def myLazyFunc(myParam: => String): String = { 
     |   println("executing myLazyFunc"); 
     |   myParam; 
     | }
myLazyFunc: (myParam: => String)String

scala> myFunc(myParamFunc)
getting parameter
executing myFunc
res5: String = a string

scala> myLazyFunc(myParamFunc)
executing myLazyFunc
getting parameter
res6: String = a string

Multiple Parameter Lists

scala> def myMultiFunc(myParam: String)(myOtherParam: Int): String = 
     |   s"$myParam:$myOtherParam"
myMultiFunc: (myParam: String)(myOtherParam: Int)String

scala> myMultiFunc("a string")(1)
res7: String = a string:1

Can be curried

scala> val mySingleFunc = myMultiFunc("a string")(_)
mySingleFunc: Int => String = <function1>

scala> mySingleFunc(1)
res8: String = a string:1

Anonymous Functions

A function by another name?

scala> val x = (i: Int) => s"got $i" 
x: Int => String = <function1>

scala> x(1)
res9: String = got 1

As a block

scala> val x = { i: Int => 
     |   println("Doing some stuff")
     |   s"returning $i"
     | }
x: Int => String = <function1>

scala> x(1)
Doing some stuff
res10: String = returning 1

As a procedure

scala> {
     |   println("Doing some stuff")
     |   println("doing other stuff")
     | }
Doing some stuff
doing other stuff

Data Structures

Tuples

Fixed-size groups of un-named things.

scala> val x = ("aString", 1)
x: (String, Int) = (aString,1)

scala> x._1
res0: String = aString

scala> x._2
res1: Int = 1

Can be combined with pattern matching as a "destructuring bind"

scala> val (x, y, z) = (1, 2, "aString")
x: Int = 1
y: Int = 2
z: String = aString

scala> x
res2: Int = 1

scala> y
res3: Int = 2

scala> z
res4: String = aString

Classes

Can be abstract.

scala> abstract class Vehicle {
     |   def name: String
     |   def maxSpeed: Int
     | }
defined class Vehicle

Can inherit from other classes.

scala> class Car(val name: String) extends Vehicle {
     |   val maxSpeed = 10
     | }
defined class Car

Traits

Can provide implementations.

scala> trait Flyable {
     |   def maxAirSpeed: Int
     |   def maxGroundSpeed(windSpeed: Int): Int = maxAirSpeed - windSpeed
     | }
defined trait Flyable

Can be inherited

scala> class MyAirPlane(val maxAirSpeed: Int) extends Flyable
defined class MyAirPlane

Can be mixed in

scala> class MyAirCar(name: String) extends Car(name) {
     |   val maxAirSpeed = 100
     | }
defined class MyAirCar

Instantiating Classes

Instantiating Concrete Types

scala> val myCar = new Car("honda")
myCar: Car = Car@508a2227

Can be instantiated anonymously

scala> val myAirplane = new Vehicle with Flyable {
     |   val name = "my airplane"
     |   val maxAirSpeed = 120
     |   override def maxSpeed = maxAirSpeed
     | }
myAirplane: Vehicle with Flyable{val name: String; val maxAirSpeed: Int} = $anon$1@c4745ff

scala> val myBird = new Flyable { val maxAirSpeed = 100 }
myBird: Flyable{val maxAirSpeed: Int} = $anon$1@1a0a5600

Singleton Objects

  • There can only be one. Mostly.
  • Instantiated at JVM startup.
  • Replaces java static methods and classes.
  • Makes a nice place to put pure functions
scala> object MySingleton {
     |   val name = "i am a singleton"
     | }
defined object MySingleton

scala> MySingleton.name
res5: String = i am a singleton

Companion Objects

  • Must be defined in the same file as their class.
  • Included in implicit search
  • Often used for "static" methods
  • Provides apply and unapply method syntax sugar
class MyClass {
  import MyClass._
  var myState = 0

  def mutate: Unit = myState = mutateTheState(myState)
}

object MyClass {
  def mutateTheState(i: Int): Int = i * 2
  def apply(): MyClass = new MyClass()
}

val x = MyClass()

Case Classes

"Product Types". Adds apply, copy, hashcode and equals.

scala> case class MyCaseClass(a: Int, b: String)
defined class MyCaseClass

scala> val x = MyCaseClass(1, "a class") 
x: MyCaseClass = MyCaseClass(1,a class)

all members are public and immutable

scala> val y = x.a 
y: Int = 1

scala> x.a = 2
<console>:15: error: reassignment to val
       x.a = 2
           ^

copy method provided

scala> val x2 = x.copy(a=2) 
x2: MyCaseClass = MyCaseClass(2,a class)

value-wise comparisons for set membership and equality

scala> x == x2 
res6: Boolean = false

scala> val x3 = x2.copy(a=1)
x3: MyCaseClass = MyCaseClass(1,a class)

scala> x3 == x // values are now same
res7: Boolean = true

Algebraic Data Types

"Sum Types". Typeful answer to enumerations, etc.

sealed trait VehicleType
object VehicleType {
  case object Airplane extends VehicleType
  case object Boat extends VehicleType
  case object Car extends VehicleType

  val All = Set(Airplane, Boat, Car)
}
val myVehicleType: VehicleType = VehicleType.Car

Syntax Details

Semicolons can replace newlines

scala> val x = Seq(1, 2)
x: Seq[Int] = List(1, 2)

scala> val y = Seq(3, 4); val z = Seq(5,6)
y: Seq[Int] = List(3, 4)
z: Seq[Int] = List(5, 6)

Infix form

If there is a single parameter to a function, . and () can be dropped

scala> object A { def myFunc(a: String) = a.length }
defined object A

scala> val a = A.myFunc("a string") 
a: Int = 8

scala> val b = A myFunc "a string"
b: Int = 8

When using "infix", beware of operator precedence

scala> A.myFunc("a string") == A.myFunc("a string")
res0: Boolean = true

scala> A myFunc "a string" == A.myFunc("a string")
<console>:14: error: type mismatch;
 found   : Boolean
 required: String
       A myFunc "a string" == A.myFunc("a string")
                           ^

Sometimes a single infix parameter might be an anonymous function

scala> val a = A.myFunc({
     |   println("I'm preparing the function parameter")
     |   "a string"
     | })
I'm preparing the function parameter
a: Int = 8

scala> val b = A.myFunc {
     |   println("I'm preparing the function parameter")
     |   "a string"
     | }
I'm preparing the function parameter
b: Int = 8

Implicits

Why implicit?

How do you extend third party libraries to meet your specific coding requirements?

  • Ruby has modules, Smalltalk let packages add to each other's classes. It's powerful but dangerous since the changes are global
  • C# 3.0 has static extension methods => more local and more restrictive, and safer
  • Scala: using implicit conversions and parameters to avoid tedious and boilerplate details.

Implicit Rules

Need implicit keyword:

implicit def convert(x: Double) = x.toString

An inserted implicit conversion must be in scope as a single identifier

scala> object MyConversions {
     |   implicit def convert1(x: String): Int = x.size
     |   implicit def convert2(x: Float): Int = x.toInt
     | }
warning: there were two feature warnings; re-run with -feature for details
defined object MyConversions

scala> val x: Int = "123"
<console>:13: error: type mismatch;
 found   : String("123")
 required: Int
       val x: Int = "123"
                    ^

scala> import MyConversions.convert1 // has convert1 in scope
import MyConversions.convert1

scala> val x: Int = "123"
x: Int = 3

scala> import MyConversions._ // has both in scope, but dangerous
import MyConversions._

, or be associated with the source or target type of the conversion

class Euro { ??? }
class Dollar {
  // toEuro() is in scope
}
object Dollar {
  implicit def toEuro(x: Dollar): Euro = ???
}

One at a time

compiler will never do convert1(convert2(x))

Explicit first

Implicit Conversion

Compiler does the trick: When compiler sees a type error, it looks for implicit conversions (therefore, increase the compilation time)

     | val x: Int = 1.9
<console>:19: error: type mismatch;
 found   : Double(1.9)
 required: Int
       val x: Int = 1.9
                    ^

Converting an expected type:

     | implicit def convert1(x: Double) = x.toInt
warning: there was one feature warning; re-run with -feature for details
convert1: (x: Double)Int

scala> val x: Int = 1.9
x: Int = 1

Converting the receiver:

scala> (1 to 4).foreach(print)
1234
scala> Map("a" -> 1, "b" -> 2)
res2: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)

Implicit Classes

scala> case class Rectangle(width: Int, height: Int)
defined class Rectangle

scala> implicit class RectangleMaker(width: Int) {
     |   def x(height: Int) = Rectangle(width, height)
     | }
defined class RectangleMaker

scala> val myRectangle = 3 x 4
myRectangle: Rectangle = Rectangle(3,4)
// Automatically generated by compiler
implicit def RectangleMaker(width: Int) = new RectangleMaker(width)

Implicit Parameters

scala> def maxList[T](elements: List[T])(implicit ordering: Ordering[T]): T =
     |   elements match {
     |     case List() =>
     |       throw new IllegalArgumentException("empty list!")
     |     case List(x) => x
     |     case x :: rest =>
     |       val maxRest = maxList(rest)(ordering)    // (ordering) is implicit
     |       if (ordering.gt(x, maxRest)) x           // ordering is explicit
     |       else maxRest
     | }
maxList: [T](elements: List[T])(implicit ordering: Ordering[T])T

implicitly[]

scala> def maxList[T](elements: List[T])(implicit foo: Ordering[T]): T =  // name doesn't matter
     |   elements match {
     |     case List() =>
     |       throw new IllegalArgumentException("empty list!")
     |     case List(x) => x
     |     case x :: rest =>
     |       val maxRest = maxList(rest)                        // (ordering) is gone
     |       if (implicitly[Ordering[T]].gt(x, maxRest)) x      // use implicitly[]  
     |       else maxRest
     | }
maxList: [T](elements: List[T])(implicit foo: Ordering[T])T

context bound: less code is better

scala> def maxList[T : Ordering](elements: List[T]): T =
     |   elements match {
     |     case List() =>
     |       throw new IllegalArgumentException("empty list!")
     |     case List(x) => x
     |     case x :: rest =>
     |       val maxRest = maxList(rest)
     |       if (implicitly[Ordering[T]].gt(x, maxRest)) x
     |       else maxRest
     | }
maxList: [T](elements: List[T])(implicit evidence$1: Ordering[T])T

Implicit Search

TypeClasses

Contexts

Thanks!

Questions?

Further Resources

1/43
An Introduction to Scala Erik LaBianca Hung Lin July 20, 2016 @easel @hunglin @easel erik.labianca@gmail.com https://www.linkedin.com/in/eriklabianca https://erik.labianca.org/scala-intro https://github.com/easel/scala-intro