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