A Brief Intro to Scala


Tim Underwood

About Me

  • Tim Underwood
  • Software Developer with 20+ years of experience
  • Perl, PHP C, C++, C#, Java, Ruby, Scala
  • ~15 years of Scala experience


  • Before Scala default languages were Ruby and Java

Dynamic vs. Static

 
Dynamic (Ruby) Static (Java)
  • Concise
  • Scriptable
  • Read-Eval-Print Loop (irb)
  • Higher Order Functions
  • Extend existing classes
  • Duck Typing
  • method_missing
  • Better IDE Support
  • Fewer Tests
  • Documentation
  • Open Source Libs
  • Performance
  • JVM Tools (VisualVM)
  • True Multi-threading

Scala

Best of both worlds!
Dynamic-Like Static
  • Concise
  • Scriptable
  • Read-Eval-Print Loop (irb)
  • Higher Order Functions
  • Extend existing classes
  • Duck Typing
  • method_missing
  • Better IDE Support
  • Fewer Tests
  • Documentation
  • Open Source Libs
  • Performance
  • JVM Tools (VisualVM)
  • True Multi-threading

Scalable language

Scala is a modern multi-paradigm programming language designed to express common programming patterns in a concise elegant and type-safe way.

Scala

  • Statically Typed
  • Runs on JVM, full inter-op with Java
  • Object Oriented
  • Functional
  • Dynamic Features

Scala is Practical

  • Can be used as drop-in replacement for Java
  • Mixed Scala/Java projects
  • Use existing Java Libraries
  • Use existing Java tools (Ant, Maven, JUnit, etc.)
  • Decent IDE Support (IntelliJ)

Scala is Concise

Type Inference


             
            val sum = 1 + 2 + 3                 // Int
             
            val lst = List(1, 2, 3)             // List[Int]
             
            val map = Map("abc" -> List(1,2,3)) // Map[String, List[Int]]
             
            

Explicit Types


             
            val sum: Int = 1 + 2 + 3
             
            val lst: List[Int] = List(1, 2, 3)
             
            val map: Map[String, List[Int]] = Map("abc" -> List(1,2,3))
             
            

Higher Level

Java (pre lambdas)

            // Check if string has uppercase character
            boolean hasUpperCase = false;
            for(int i = 0; i < name.length(); i++) {
                if(Character.isUpperCase(name.charAt(i))) {
                    hasUpperCase = true;
                      break;
                }
            }
            

Higher Level

Scala

            // Check if string has uppercase character
            val hasUpperCase = name.exists(_.isUpperCase)
            

Less Boilerplate

Java - Mutable POJO

            public class Person {
              private String name;
              private int age;
              public Person(String name, Int age) {  // constructor
                this.name = name;
                this.age = age;
              }
              public String getName() {              // name getter
                return name;
              }
              public int getAge() {                  // age getter
                return age;
              }
              public void setName(String name) {     // name setter
                this.name = name;
              }
              public void setAge(int age) {          // age setter
                this.age = age;
              }
            }
            

Less Boilerplate

Scala - Mutable Class

            class Person(var name: String, var age: Int)
            

Less Boilerplate

Scala

Override Getter and/or Setter as-needed


            class Person(var name: String, private var _age: Int) {
              def age = _age             // Getter for age
              def age_=(newAge:Int) {    // Setter for age
                println("Changing age to: "+newAge)
                _age = newAge
              }
            }
            

Variables and Values

var - can reassign      val - only assign once


            // variable
            var foo = "foo"
    
            // this is fine since foo is a var
            foo = "bar"
            
            // value
            val bar = "bar"  // Java equivalent: final String bar = "bar"
            
            // does not compile since bar is a val
            bar = "foo"
            
            

Scala is Object Oriented

Pure O.O.


            // Every value is an object
            1.toString
            
            // Every operation is a method call
            1 + 2 + 3 <=> (1).+(2).+(3)
            
            // Can omit . and ( )
            "abc" charAt 1 <=> "abc".charAt(1)
            

Classes


            // Classes (and abstract classes) like Java
            abstract class Language(val name: String) {
              override def toString = name
            }
            
            // Example implementations
            class Scala extends Language("Scala")
            
            // Anonymous class
            val scala = new Language("Scala") { /* add stuff here */ }
            

Traits


            // Like interfaces in Java
            trait Language {
            
              val name: String
            
              // But allow implementation
              override def toString = name
            }
            

Traits


            trait JVM { 
              override def toString = super.toString+" runs on JVM" }
            
            trait Static {
              override def toString = super.toString+" and is Static" }
            
            // Traits are stackable
            class Scala extends Language with JVM with Static {
              val name = "Scala"
            }
            
            println(new Scala) // "Scala runs on JVM and is Static"
            

Singleton Objects


            // Replaces static methods from Java
            // Can extend/implement classes & traits
            
            object Hello {
              def world = println("Hello World"}
            }
            
            Hello.world // Prints: Hello World
            

Scala is Functional

First Class Functions


            // Create anonymous function
            val plusOne = (x: Int) => x + 1
            
            // Calling the anonymous function
            plusOne(5)
            

Closures


            var foo = 1
            
            // plusFoo can reference any values/variables in scope
            val plusFoo = (x: Int) => x + foo
            
            plusFoo(5) // 6
            
            // Changing foo changes the return value of plusFoo 
            foo = 5
            plusFoo(5) // 10
            

Higher Order Functions


            val plusOne = (x: Int) => x + 1
            val nums = List(1,2,3)
            
            // map takes a function: Int => T
            nums.map(plusOne)     // List(2,3,4)
            
            // Inline Anonymous
            nums.map(x => x + 1)  // List(2,3,4)
            
            // Short form using a placeholder
            nums.map(_ + 1)       // List(2,3,4)
            

Higher Order Functions


            val nums = List(1,2,3,4)
            
            // A few more examples for List class
            nums.exists(_ == 2)        //  true
            nums.find(_ == 2)          //  Some(2)
            nums.indexWhere(_ == 2)    //  1
            nums.reduceLeft(_ + _)     //  10
            nums.foldLeft(100)(_ + _)  //  110
            
            // Many more in collections library
            

Higher Order Functions


            // functions as parameters
            def call(f: Int => Int) = f(1)
            
            call(plusOne)     //  2
            call(x => x + 1)  //  2
            call(_ + 1)       //  2
            

Higher Order Functions


            // functions as parameters
            def each(xs: List[Int], fun: Int => Unit): Unit = {
              if (!xs.isEmpty) {
                fun(xs.head)
                each(xs.tail, fun)  // tail recursion is optimized!
              }
            }
            
            each(List(1,2,3), println)
                //  1
                //  2
                //  3
            
            

Higher Order Functions

Using generics & pattern matching
        
            def each[T](xs: List[T], fun: T => Unit): Unit = xs match {
              case Nil =>
              case head :: tail => fun(head); each(tail, fun)
            }
            
            each(List(1,2), println)
                //  1
                //  2
            
            each(List("foo", "bar"), println)
                //  foo
                //  bar
            

Pattern Matching

        
            def what(any: Any) = any match {
              case i: Int    => "It's an Int"
              case s: String => "It's a String"
              case _         => "I don't know what it is"
            }
            
            what(123)     //  "It's an Int"
            what("hello") //  "It's a String"
            what(false)   //  "I don't know what it is"
            
            

Pattern Matching

        
            val nums = List(1,2,3)
            
            // Pattern matching to create 3 vals
            val List(a,b,c) = nums
            
            a    //  1
            b    //  2
            c    //  3
            

Immutable Types

        
            // Immutable types by default
            var nums = Set(1,2,3)
            nums += 4  //  nums = nums.+(4) -- creates new set
            
            // Mutable types available
            import scala.collection.mutable._
            
            val nums = Set(1,2,3)
            nums += 4  //  nums.+=(4) -- updates existing set
            

scala.collection

scala.collection types

scala.collection.immutable

scala.collection.immutable types

scala.collection.mutable

scala.collection.mutable types

Or Use Existing Java Collections

Scala is Dynamic*

(*Okay not really, but it has lots of features typically only found in Dynamic languages)

Scala is Scriptable

        
            bash$ cat hello.sc
            println("Hello, World!")
    
            bash$ scala-cli hello.sc
            Hello, World!
            

Read-Eval-Print Loop

        
            bash$ scala
            Welcome to Scala 3.3.1 (17, Java OpenJDK 64-Bit Server VM).
            Type in expressions for evaluation. Or try :help.
    
            scala> class Foo { def bar = "baz" }
            // defined class Foo
    
            scala> val f = new Foo
            val f: Foo = Foo@213835b6
    
            scala> f.bar
            val res0: String = baz
            

Structural Typing

Type-Safe Duck Typing

            import reflect.Selectable.reflectiveSelectable
    
            def doTalk(any: {def talk: String}) = {
              println(any.talk)
            }
            
            class Duck { def talk = "Quack" }
            class Dog  { def talk = "Bark"  }
            
            doTalk(new Duck)  //  "Quack"
            doTalk(new Dog)   //  "Bark"
            
            

Implicit Conversions

Extend existing classes in a type-safe way

            // Goal: Add isBlank method to String class
            implicit class RichString(s: String) {   
              def isBlank = null == s || "" == s.trim
            }
            
            // Our isBlank method is now available on Strings!
            " ".isBlank    //  true
            "foo".isBlank  //  false
            

Implicit Conversions

How it works at a high level

            // 1. Does not type check
            "abc".isBlank // ERROR
            
            // 2. Search in-scope implicits classes and methods that take
            //    a String and return a type with an isBlank method
            implicit class RichString(s: String) { def isBlank = ... }
            
            // 3. Scala converts the code to:
            new RichString("abc").isBlank
            
            

method_missing


            import scala.language.dynamics
            
            class Foo extends Dynamic {
                def applyDynamic(name: String)(args: Any*) = {
                    println("called: "+name+"("+args.mkString(",")+")")
                }
            }
    
            val f = new Foo
            f.helloWorld()    //  called: helloWorld()
            f.hello("world")  //  called: hello(world)
            f.bar(1,2,3)      //  called: bar(1,2,3)
            

Scala has tons of other cool stuff!

Default Parameter Values


            def hello(foo: Int = 0, bar: Int = 0) = {
              println("foo: "+foo+"  bar: "+bar)
            }
            
            hello()        //  foo: 0  bar: 0
            hello(1)       //  foo: 1  bar: 0
            hello(1,2)     //  foo: 1  bar: 2
            
            

Named Parameters


            def hello(foo: Int = 0, bar: Int = 0) = {
              println("foo: "+foo+"  bar: "+bar)
            }
            
            hello(bar = 6)           //  foo: 0  bar: 6
            hello(foo = 7)           //  foo: 7  bar: 0
            hello(foo = 8, bar = 9)  //  foo: 8  bar: 9
            

Everything Returns a Value


            val a = if (true) "yes" else "no"
            
            val b = try {
              "foo"
            } catch { 
              case _ => "error"
            }
            
            val c = { 
              println("hello")
              "foo"
            }
            

Lazy Vals


            // initialized on first access
            lazy val foo = {
              println("init")
              "bar"
            }
            
            foo  //  init
            foo  //
            foo  //
            

Nested Functions


            def outer() = {
                var msg = "foo"
                def one() = {
                    def two() = {
                        println(msg)
                    }
                    two()
                }
                one()
            }
            

By-Name Parameters


            def log(doLog: Boolean, msg: => String) {
              if (doLog) {
                msg  // evaluates msg
                msg  // evaluates msg again!
              }
            }
            
            def foo: String = { println("in foo"); "Foo" }
            
            log(true, foo+" Bar")   // foo called twice
    
            log(false, foo+" Bar")  // foo never called
            

Many More Features


            @foo def hello = "world"    // annotations
    
            case class Foo(bar: String) // case classes
    
            def foo(a: Int)(b: Boolean) // currying
    
            fun(123, _)                 // partially applied functions
    
            (1, "foo", false)           // tuples
    
            for (i <- 1 to 5 if i % 2 == 0) yield i // for comprehensions
            

Personal Experiences

  • Productive from Day 1
  • Drop in replacement for Java giving you more Ruby-like syntax and features
  • Can pickup the functional and higher-level programming concepts as you go
  • implicits are very powerful (when used properly)
Great Book for a Deep Dive into Scala
Programming in Scala Book
Programming in Scala

www.scala-lang.org