On Github easel / scala-intro
July 20, 2016
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
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
scala> val x = 1 x: Int = 1
scala> val x: Long = 2 x: Long = 2 scala> val y = 2:Long y: Long = 2
scala> type MyLong = Long defined type alias MyLong scala> val x: MyLong = 2 x: MyLong = 2
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 = ()
scala> val x = if(1 == 1) "true" else "false" x: String = true
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
scala> val x = 1 match {
     |   case 1 => "true"
     |   case _ => "false"
     | }
x: String = true
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
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
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
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
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
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
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
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
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
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 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
scala> object MySingleton {
     |   val name = "i am a singleton"
     | }
defined object MySingleton
scala> MySingleton.name
res5: String = i am a singleton
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()
"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
"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
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)
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
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")
                           ^
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
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
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)
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)
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
Further Resources