What is Scala ?

Why Scala?

Why Scala? Multi Paradigm

Why Scala? Bleeding Edge

Why Scala?

Plan: Rest of the Quarter

Whirlwind Overview + Advanced Features

  1. "Crash course": Expressions, Values, and Types
  1. Classes, Subtyping, and Generic Polymorphism
  1. Inheritance, Traits, and Mixins
  1. Implicits and Type-Classes

Many Resources

Online (These are highly recommended!)

Many Resources

Books

Let's Start the "Crash Course"!

Scala 101: Expressions & Types

Like ML, Scala is statically typed

Scala 101: Read-Eval-Print Loop

The easiest way to get started is with Scala's REPL

$ scala
Welcome to Scala version 2.10.3 ...
Type in expressions to have them evaluated.
Type :help for more information.

scala> 

Scala 101: Read-Eval-Print Loop

Enter expressions and have them evaluated.

scala> 2
res0: Int = 2

scala> 2 + 3
res1: Int = 5

Everything is an "Object"

So when you type:

2 + 3

the compiler sees the method call:

2.+(3)

So + is just a method call! (As are many other things...)

Everything is an "Object": Overloading

Furthermore, unlike ML (and like Java) Scala supports "overloading".

scala> 2 + "cat"
res3: String = 2cat

scala> 2 + 3.0 
res4: Double = 5.0 

Each of these calls the appropriate method on the receiver Int.

Everything is an "Object": Overloading

But don't get carried away...

scala> 2 + true

<console>:8: error: overloaded method value + with alternatives:
  (x: Double)Double <and>
  (x: Float)Float <and>
  (x: Long)Long <and>
  (x: Int)Int <and>
  (x: Char)Int <and>
  (x: Short)Int <and>
  (x: Byte)Int <and>
  (x: String)String
 cannot be applied to (Boolean)
       2 + true
         ^

Basic Data

You have your basic built-in types...

scala> 4.2 * 6.7
res8: Double = 28.14

scala> true || false
res9: Boolean = true

scala> 'c'
res10: Char = c 

Basic Data

Strings are borrowed from Java...

scala> "cat"
res25: java.lang.String = cat

scala> "cat" + "dog"
res26: java.lang.String = catdog

scala> "cat" * 3 
res26: java.lang.String = catcatcat

Basic Data

... and you can take whatever you like from Java.

scala> import java.util.Date
import java.util.Date

scala> new Date()
res2: java.util.Date = Thu Nov 08 17:40:50 PST 2012

So every Java library is usable in Scala!

Result Variables

Notice that the REPL gives names to resulting values.

scala> "Cool!"
res2: String = Cool!

scala> res2
res3: String = Cool!

Speaking of Variables...

Variables

Two ways to introduce variables:

Immutable Variables

scala> val x = 2
x: Int = 2
scala> x = x + 1
<console>:8: error: reassignment to val
       x = x + 1
         ^

If you absolutely must: Mutable Variables

scala> var z = 1 
z: Int = 1

scala> z += 1

scala> z
res1: Int = 2

scala> z = 3
z: Int = 3

We will revisit val and var when we talk about objects and fields.

Compound Data: Tuples

Scala comes equipped with modern amenities like tuples.

scala> val t = ("cat", 12)
t: (java.lang.String, Int) = (cat,12)

scala> val t2 = ("mickey", 12, "mouse")
t2: (java.lang.String, Int, java.lang.String) 
    = (mickey,12,mouse)

scala> val t3 = ("mickey", (12, "mouse"))
t3: (java.lang.String, (Int, java.lang.String)) 
    = (mickey,(12,mouse))

Tuple types are written (T1, T2) and (T1, T2, T3) and so on
(unlike (T1 * T2) as in OCaml).

Compound Data: Tuples

To access the tuple elements, you can use pattern matching

scala> t
res7: (java.lang.String, Int) = (cat,12)

scala> val (p, q) = t
p: java.lang.String = cat
q: Int = 12

or the tuple field accessors ._1 or ._2, etc.

scala> t._1
res5: java.lang.String = cat

scala> t._2
res6: Int = 12

Compound Expressions

You can sequence expressions to get bigger expressions:

scala> :paste
// Entering paste mode (ctrl-D to finish)

{ println("hello world")
  2 + 8 }

// Exiting paste mode, now interpreting.

hello world
res0: Int = 10

Paste-mode lets you enter multi-line expressions in REPL.

Compound Expressions

Functions

Functions

Many ways to define functions. One way:

def inc(x: Int): Int = {
  x + 1
}

Notice explicit argument and return types...

Call a function in the usual way (parentheses required):

scala> inc(4)
res3: Int = 5

Functions

No explicit return, just a result expression (like ML):

def adder(x: Int, y: Int): Int = {
  println("adder args: (x=%d, y=%d)".format(x, y))
  val z = x + y
  println("adder ret: (z=%d)".format(z))
  z
}

Block evaluates to last expression

scala> adder(2, 3)
adder args: (x=2, y=3)
adder ret: (z=5)
res6: Int = 5

Curried Functions

def curriedAdder(x: Int)(y: Int): Int = {
  println("curriedAdder args: %d %d".format(x, y))
  x + y 
}

Curried functions require parens around each argument!

scala> curriedAdder(2)(3)
curriedAdder args: 2 3
res7: Int = 5

Curried Functions

What about partial application as in OCaml?

scala> curriedAdder(2)(3)
curriedAdder args: 2 3
res7: Int = 5
scala> val add2 = curriedAdder(2)
error: missing arguments for method curriedAdder ...

Curried Functions

To partially apply, need _ for all other arguments:

scala> val plus2 = curriedAdder(2)(_)
plus2: Int => Int = <function1>

scala> plus2(3)
curriedAdder args: 2 3
res8: Int = 5

Curried Functions

You can partially apply any of the arguments...

scala> val plus3 = curriedAdder(_:Int)(3)
plus3: Int => Int = <function1>

scala> plus3(2)
curriedAdder args: 2 3
res9: Int = 5

Whoa!

Note: sometimes Scala will complain and ask for a type annotation.

Functions with No Name

A.k.a anonymous functions, like OCaml's fun x -> e

scala> (x: Int) => x + 1 
res3: (Int) => Int = <function1>

scala> (x: Int, y: Int) => x + y 
res2: (Int, Int) => Int = <function2>

Notice the types of the functions

Functions with No Name

Can call directly:

scala> ((x: Int) => x + 1)(8)
res3: Int = 9

Usually makes sense to bind to a name first:

scala> val inc = (x: Int) => x + 1
inc: (Int) => Int = <function1>

scala> inc(5)
res5: Int = 6

Functions are Objects, Too!

Functions are Objects, Too!

Functions are just objects with apply method.

scala> object inc { def apply(x: Int) = x + 1 }

Call them like so:

scala> inc(5)
res: Int = 6

Functions are Objects, Too!

Functions are just objects with apply method.

When you write f(args) Scala reads f.apply(args)

Functions are Objects, Too!

Anything with apply can be called.

scala> val str = "megalomaniac"
str: String = megalomaniac

scala> str(2)
res: Char = g

So far: Basic Data, Variables, Functions

Next: Control Expressions

Control Expressions

The usual suspects ...

Control Expressions: If-Else

def fac(n: Int): Int = {
  if (n > 0) { n * fac (n-1) } else { 1 }
}

The {...} around then and else expressions can sometimes be omitted.

def fac(n: Int): Int = {
  if (n > 0) n * fac (n-1) else 1
}

But, it's okay to always write {...}.

Control Expressions: Pattern Matching!

Scala has pattern matching, too:

def fac(n: Int): Int =
  n match {
    case n if (n > 0) => n * fac(n-1) 
    case _            => 1
  }

More on this later...

Digression: About Those Types...

Unlike ML, you have to write down some types in Scala.

Must write argument types for all functions.

Must write return types for recursive functions.
(Of course, you can also write them for non-recursive functions.)

scala> def fac(n: Int) =
     |   n match {
     |     case n if (n > 0) => n * fac(n-1) 
     |     case _            => 1
     |   }
error: recursive method fac needs result type ...

Scala's Local Type Inference figures out the rest!

Digression: Zero-Argument Functions...

scala> def f() = 1
f: ()Int

scala> f()
res1: Int = 1

Can be called without parentheses:

scala> f
res2: Int = 1

Control Expressions: Try-Catch

Put the body expression inside {...}.

import java.io.File 
 
def fileContents(file: String): List[String] = {
  val f = new java.io.File(file)
  try {
    scala.io.Source.fromFile(f).getLines().toList
  } catch {
    case e: java.io.FileNotFoundException => {
      println("WTF! No such file exists")
      List()
    }
  }
}

Control Expressions: Try-Catch (contd.)

Create a file called file.txt with some lines of text.

Try running:

scala> val lines = fileContents("file.txt")
lines: List[String] = List(Hello, world)

Try running it with a junk file:

scala> fileContents("foobar")
WTF! No such file exists
res10: List[String] = List()

Control Expressions: while-loops

Old-school while loops:

def fac(n: Int) = {
  var res   = 1
  var count = n
  while (count > 0) {
    res   *= count
    count -= 1
  }
  res
}

Control Expressions: for-loops

And, of course, for-loops, too:

scala> for (i <- 1 to 5) println(i)
1
2
3
4
5

Control Expressions: for-loops

Or, if you want to step by something other than 1 ...

scala> for (i <- 1 to 5 by 2) println(i)
1
3
5

Control Expressions: for-loops

Or, count in the other direction ...

scala> for (i <- 5 to 1 by -1) println(i)
5
4
3
2
1

Control Expressions: for-loops

Actually, for-loops are quite special.

scala> for (i <- "special") println(i)
s
p
e
c
i 
a
l

Hmm, what's going on?

Scala Collections (Batteries Included)

Batteries Included: Collections

Scala bundled with with a rich set of Collections

Some are immutable. Some are mutable.

Batteries Included: Arrays

(Mutable) Arrays

scala> val a = Array("cow", "dog", "mouse")
res14: Array[java.lang.String] = Array(cow, dog, mouse)

Note the type: Array[String].

Arrays have a fixed length

scala> a.length
res15: Int = 3

Batteries Included: Arrays

(Mutable) Arrays can be randomly accessed.

scala> a(2)
res16: java.lang.String = mouse 

Batteries Included: Arrays

(Mutable) Arrays can be randomly updated.

scala> a(0) = "aardvark"

scala> a
res19: Array[java.lang.String] =
         Array(aardvark, dog, mouse)

Batteries Included: for-loops

(Mutable) Arrays can also be looped over...

scala> for (animal <- a) println(animal)
aardvark
dog
mouse

"Off-by-one" errors BEGONE!

But immutable better if you can help it...

Batteries Included: Lists

Batteries Included: Lists

(Immutable) Lists

scala> val xs = List(1,2,3,4)
xs: List[Int] = List(1, 2, 3, 4)

scala> val ys = List("cat", "dog", "moose", "goose")
ys: List[java.lang.String] =
      List(cat, dog, moose, goose)

Cannot change a list!

scala> ys(0) = "kite"
error: value update is not a member of
       List[java.lang.String] ...

Batteries Included: Lists

Quite a bit like ML lists.

scala> val zs = "chicken" :: ys
zs: List[java.lang.String] =
      List(chicken, cat, dog, moose, goose)

Can be accessed via pattern-matching:

def listConcat(xs: List[String]): String =  
  xs match {
    case Nil  => "" 
    case h::t => h + listConcat(t)
  }

Which you can call like so:

scala> listConcat(zs)
res10: chickencatdogmoosegoose

Batteries Included: Lists

You can also append lists.

scala> List(1,2,3) ++ List(4,5,6)
res11: List[Int] = List(1,2,3,4,5,6)

Alternate syntax:

scala> List(1,2,3) ::: List(4,5,6)
res11: List[Int] = List(1,2,3,4,5,6)

Batteries Included: Lists

And you can loop over lists, too:

scala> for (animal <- zs) println(animal)
chicken
cat
dog
moose
goose

Batteries Included: Hash-Maps

Batteries Included: Hash-Maps

Key-Value Maps (immutable by default)

scala> val numNames =
         Map("one" -> 1, "two" -> 2, "three" -> 3)
numNames:
  scala.collection.immutable.Map[String,Int] 
= Map(one -> 1, two -> 2, three -> 3)

Batteries Included: Hash-Map Lookup

Key-Value Maps (immutable by default)

You can lookup the value of a key much like arrays:

scala> numNames("three")
res12: Int = 3

Batteries Included: Hash-Maps Lookup

Key-Value Maps (immutable by default)

If the value doesn't exist though...

scala> numNames("nine")
java.util.NoSuchElementException: key not found: nine
...

Exception!

Batteries Included: Hash-Maps Membership

Key-Value Maps (immutable by default)

Moral, look before you leap:

scala> numNames.contains("nine")
res13: Boolean = false

Or, using the cleaner notation:

scala> numNames contains "nine"
res14: Boolean = false

Batteries Included: Hash-Maps Adding Keys

Key-Value Maps (immutable by default)

Would be nice to extend a map with new key-value bindings...

How do you think it's done?

Batteries Included: Hash-Maps Adding Keys

Key-Value Maps (immutable by default)

Would be nice to extend a map with new key-value bindings...

scala> numNames + ("nine" -> 9)
res15:
  scala.collection.immutable.Map[String,Int] 
= Map(one -> 1, two -> 2, three -> 3, nine -> 9)

Note: the above is a brand new map ...

scala> numNames contains "nine"
res16: Boolean = false

Batteries Included: Hash-Maps Adding Keys

Key-Value Maps (immutable by default)

Would be nice to extend a map with new key-value bindings ...

scala> val newMap = numNames + ("nine" -> 9)
newMap:
  scala.collection.immutable.Map[String,Int] 
= Map(one -> 1, two -> 2, three -> 3, nine -> 9)

... So, bind the result to a new variable.

scala> newMap("nine")
res17: Int = 9

Batteries Included: Mutable Hash-Maps (!)

Batteries Included: Mutable Hash-Maps

(Pssst.) There are mutable Key-Value Maps, too...

scala> import scala.collection.mutable.HashMap

scala> val mmap : HashMap[String, Int] = HashMap()
scala> mmap += "mon" -> 1 
scala> mmap += "tue" -> 2 
scala> mmap += "wed" -> 3

scala> mmap("tue")
res18: Int = 2

Note: type parameters for the key (String) and value (Int)

Crash Course (continued), Feb 25

News

Recap:
Expressions, Variables, Functions, Collections

Recall: Immutable vs. Mutable

Variables

Maps

CLICKER: What is the value of res?

import scala.collection.mutable.HashMap
val mutMap: HashMap[String, Int] = HashMap()
mutMap += "mon" -> 1 
mutMap += "mon" -> 2 
val res = mutMap("mon")    // mutMap.apply("mon")

A. No value, Type Error (cannot update val)

B. No value, Runtime Exception (key not found)

C. 1: Int

D. 2: Int

E. None: Option[Int]

CLICKER: What is the value of res?

import scala.collection.immutable.Map
var immutMap : Map[String, Int] = Map()
var other = immutMap
immutMap  = immutMap + ("mon" -> 1)
immutMap  = immutMap + ("mon" -> 2)
val res   = other("mon")

A. No value, Type Error

B. No value, Runtime Exception (NotFound)

C. 1: Int

D. 2: Int

E. None: Option[Int]

CLICKER: What is the value of res?

var mutMap : HashMap[String, Int] = HashMap()
var other  = mutMap
mutMap    += "mon" -> 1
mutMap    += "mon" -> 2
val res    = other("mon")

A. No value, Type Error

B. No value, Runtime Exception (NotFound)

C. 1: Int

D. 2: Int

E. None: Option[Int]

Checking Key Membership

Nasty run-time exceptions...

scala> var mutMap = HashMap[String, Int]("a" -> 1)
scala> mutMap("b")
... Exception ...

Defensive programming:

scala> if (mutMap contains "b") mutMap("b") else -1
res0: Int = -1

Checking Key Membership

Instead, the get method returns an option type!

scala> mutMap.get("a")
res1: Option[Int] = Some(1)

scala> mutMap.get("b")
res2: Option[Int] = None

Pattern match result:

scala> mutMap.get("b") match {
     |   case Some(n) => n
     |   case None    => -1
     | }
res3: Int = -1

CLICKER: What is the value of res?

var mutMap : HashMap[String, Int] = HashMap()
var other = mutMap + ("mon" -> 1)
val res   = (other.get("mon"), mutMap.get("mon"))

A. No value, Type Error

B. (None, None) : (Option[Int], Option[Int])

C. (Some(1), None) : (Option[Int], Option[Int])

D. (None, Some(1)) : (Option[Int], Option[Int])

E. (Some(1), Some(1)) : (Option[Int], Option[Int])

What is the value of res?

var mutMap : HashMap[String, Int] = HashMap()
var other = mutMap + ("mon" -> 1)
val res   = (other.get("mon"), mutMap.get("mon"))

Drumroll please...

What if we tried this instead, without the other variable?

var mutMap : HashMap[String, Int] = HashMap()
mutMap    = mutMap + ("mon" -> 1)
val res   = (other.get("mon"), mutMap.get("mon"))

Moral: Mutation is Tricky

How to operate on collections?

How to operate on collections?
Higher Order Functions!

Collections and Higher-Order Functions

All collection objects equipped with HOFs!

HOFs + Collections: filter

All collection objects equipped with HOFs!

scala> "MaSsIvEaTtAcK".filter((c: Char) => c.isUpper)
res19: String = MSIETAK

Can omit argument type in this case:

scala> "MaSsIvEaTtAcK".filter(c => c.isUpper)
res19: String = MSIETAK

Or, with equivalent, simpler syntax for HOFs:

scala> "MaSsIvEaTtAcK".filter(_.isUpper)
res20: String = MSIETAK

HOFs + Collections: filter

All collection objects equipped with HOFs!

scala> List(1,2,3,4,5,6,7,8).filter(_ % 2 == 0)
res21: List[Int] = List(2,4,6,8) 

scala> Array(1,2,3,4,5,6,7,8).filter(_ % 2 == 0)
res21: Array[Int] = Array(2,4,6,8) 

HOFs + Collections: filter

With Map, the filter is over key-value tuples:

scala> numNames.filter(kv => kv._1.length == 3)
res24: scala.collection.immutable.Map[String,Int] 
       = Map(one -> 1, two -> 2)

Or, using the shorthand:

scala> numNames.filter(_._1.length == 3)
res24: scala.collection.immutable.Map[String,Int] 
       = Map(one -> 1, two -> 2)

HOFs + Collections: filter

With Map, the filter is over key-value tuples:

Or, using an anonymous function with pattern matching:

scala> numNames.filter({case (k,v) => k.length == 3})
res23: scala.collection.immutable.Map[String,Int] 
       = Map(one -> 1, two -> 2)

HOFs + Collections: map

All collection objects equipped with HOFs!

Strings...

scala> "MaSsIvEaTtAcK".map(_.toUpper)
res24: String = MASSIVEATTACK

Lists...

scala> List(1,2,3).map(_ * 100)
res25: List[Int] = List(100, 200, 300)

Arrays...

scala> Array("cat", "dog").map(_.map(_.toUpper))
res26: Array[String] = Array(CAT, DOG)

... are all collections.

HOFs + Collections: foreach

foreach is like map but does an action, returns dummy value ().

scala> Array("cat", "dog").foreach(println(_))
cat
dog

So, what's a for-loop...?

val a = Array("cat", "dog")
for (i <- a) { println(i) }

HOFs + Collections: for-loops Revisited

for-loop is just a HOF!

Writing

for (a <- thing) { body }

is just the same as

thing.foreach(a => body)

HOFs + Collections: for-loops Revisited

So, you can loop over all collections:

scala> numNames.foreach(
         {case (k, v) => println(k + " ~~~> " + v)}) 
one ~~~> 1
two ~~~> 2
three ~~~> 3

Or, if you prefer:

scala> for ((k, v) <- numNames)
         println(k + " ~~~> " + v)
one ~~~> 1
two ~~~> 2
three ~~~> 3

HOFs + Collections: for-loops Revisited

Another example:

scala> val dir = new java.io.File(".")
dir: java.io.File = .

scala> for (f <- dir.listFiles) println(f)
./slides.markdown
./scratch.markdown
./Monoid.scala
./Freq.scala
...

HOFs + Collections: for-loops Revisited

Sometimes, want to loop over some elements and skip others.

For example, to print names of all .scala files:

scala> for (f <- dir.listFiles 
            if f.getName.endsWith(".markdown"))
       { println(f) }
./Monoid.scala
./Freq.scala

CLICKER: for-loops Revisited

Sometimes, want to loop over some elements and skip others.

for (x <- coll if cond) { body }

This is really nice syntactic sugar for which of the following?

A. coll.filter(x => body)

B. coll.filter(x => cond).foreach(x => body)

C. coll.foreach(x => body)

D. coll.foreach(x => body).filter(x => body)

HOFs + Collections: for-loops Revisited

Often want to compute a collection as a value.

Enter yield.

Suppose we have an Array of Strings:

val words = Array("you", "are", "doing", "it", "wrong")

To turn it into a rant:

scala> val rant = for (w <- words) yield w.toUpperCase
rant: Array[java.lang.String] =
        List(YOU, ARE, DOING, IT, WRONG)

Note: the output is also an Array.

HOFs + Collections: for-loops Revisited

Often want to compute a collection as a value.

Enter yield.

Works for any collection:

scala> for (w <- fileContents("file.txt"))
         yield w.toUpperCase

res: List[String] = List(HELLO, WORLD)

CLICKER: for-loops Revisited

Often want to compute a collection as a value.

for (x <- coll) yield expr 

This is really nice syntactic sugar for which of the following?

A. coll.foreach(x => expr)

B. coll.filter(x => expr)

C. coll.map(x => expr)

HOFs + Collections: for-loops Revisited

Sometimes want to compute a collection as a value ...

... after some processing.

For example, to find all .scala files in a directory:

scala> val scalaFiles =
         for (f <- dir.listFiles 
              if f.getName.endsWith(".scala")) 
           yield f

scalaFiles: Array[java.io.File] =
              Array(./Monoid.scala, ./Freq.scala)

HOFs + Collections: for-loops Revisited

Sometimes want to compute a collection as a value ...

... after some processing.

scala> import scala.io.Source._
scala> val fileSizes =
         for (f <- dir.listFiles 
              if f.getName.endsWith(".scala")) 
           yield fromFile(f).length

fileSizes: Array[Int] = Array(349, 406)

HOFs + Collections: for-loops Revisited

Sometimes want to compute a collection as a value ...

... after some processing.

for (x <- coll if cond) yield expr 

Is really another way of writing

coll.filter(x => cond).map(x => expr)

HOFs + Collections: Nested for-loops

You can nest for-loops, too:

scala> for (i <- 1 to 3; j <- 1 to 3) { 
         println ("i = %d and j = %d" format (i, j))
       }
i = 1 and j = 1
i = 1 and j = 2
i = 1 and j = 3
i = 2 and j = 1
i = 2 and j = 2
i = 2 and j = 3
i = 3 and j = 1
i = 3 and j = 2
i = 3 and j = 3

HOFs + Collections: Nested for-loops

You can nest for-loops, too.

And then return a collection as the result:

scala> for (i <- 1 to 3; j <- 1 to 3) yield (i, j)
res:
  scala.collection.immutable.IndexedSeq[(Int, Int)] 
= Vector((1,1), (1,2), (1,3),
         (2,1), (2,2), (2,3),
         (3,1), (3,2), (3,3))

CLICKER: Nested for-loops

Nest for-loops, and return a collection after filtering:

scala> val res = for (i <- List(1,2,3);  
                      j <- List(4,5,6) 
                      if i + j == 7) 
                 yield (i, j)

What does this produce?

A. List()

B. List((1,6),(2,5),(3,4))

C. List(1,2,3,4,5,6)

D. List((1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6))

E. List((1, 4), (2, 5), (3, 6))

HOFs + Collections: Nested for-loops

Nest for-loops, and return a collection after filtering.

Works for any collection.

scala> val res = for (i <- 1 to 3;
                      j <- 1 to 3
                      if i < j) 
                 yield (i, j)

res: IndexedSeq[(Int, Int)] =
       Vector((1,2), (1,3), (2,3))

So far, inner loop independent of outer but...

CLICKER: Nested for-loops

The inner loop can also depend on outer loop:

scala> val ll = List(List(1,2), List(3,4))
scala> val res = for (xs <- ll;
                      x  <- xs) 
                 yield x 

What is the value of res?

A. List(1, 3)

B. List(List(1,2), List(3,4))

C. List((1,3), (1,4), (2,3), (2,4))

D. List(1,2,3,4)

E. List()

HOFs + Collections: Nested for-loops

Another example:

scala> for (w <- List("cat", "dog", "mouse"); c <- w)
       yield c
res: List[Char] =
       List(c, a, t, d, o, g, m, o, u, s, e)

Note the output type is also List ...

... like the top-level collection!

HOFs + Collections: Nested for-loops

Another example:

scala> for (w <- Array("cat", "dog", "mouse"); c <- w)
       yield c
res: Array[Char] =
       Array(c, a, t, d, o, g, m, o, u, s, e)

Note the output type is also Array ...

... like the top-level collection!

HOFs + Collections: for-loops Revisited

Wait a minute! Remember this?

scala> for (i <- 1 to 5) println(i)
1
2
3
4
5

How does it work ?

HOFs + Collections: for-loops Revisited

Wait a minute! Remember this?

1 to 5 is a method call 1.to(5)

scala> 1 to 5
res: scala.collection.immutable.Range.Inclusive =
       Range(1, 2, 3, 4, 5)

HOFs + Collections: for-loops Revisited

If you want a step:

scala> 1 to 10 by 2   // 1.to(10).by(2)
res3: scala.collection.immutable.Range =
        Range(1, 3, 5, 7, 9)

scala> 10 to 1 by -1  // 10.to(1).by(-1)
res4: scala.collection.immutable.Range =
        Range(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

scala> 10 to 1 by -2  // 10.to(1).by(-1)
res5: scala.collection.immutable.Range =
        Range(10, 8, 6, 4, 2)

You can do a LOT with collections!

So, what does this do?

List(1,2,3,4,5).foldLeft(0)((x1, x2) => x1 + x2)

You can do a LOT with collections!

So, what does this do?

List(1,2,3,4,5).foldLeft(0)(_ + _)

Here, (_ + _) is shorthand for

(x1, x2) => x1 + x2 

BTW, you can do a LOT with collections!

And, what does this do?

def foo(n: Int, k: Int) = 
  (1 to k).map(_ => n).foldLeft(1)(_ * _)

Put things together: Frequencies

Let's write a Frequency Finder

How often does a Char appear in a String?

def freq(str: String): HashMap[Char, Int] = {
  val freqMap = new HashMap[Char, Int]
  for (c <- str) {
    freqMap(c) = 1 + freqMap.getOrElse(c, 0)
               // if(freqMap contains c){freqMap(c)} 
               // else { 0 } 
  }
  freqMap
}

Let's write a Polymorphic Frequency Finder

Let's generalize a bit (make it polymorphic).

def freq[A](xs: Iterable[A]): HashMap[A, Int] = {
  val freqMap = new HashMap[A, Int]
  for (x <- xs) {
    freqMap(x) = 1 + freqMap.getOrElse(x, 0)
  }
  freqMap
}

Iterable[A] describes objects that can be iterated over...

Let's write a Polymorphic Frequency Finder

Can run it on Strings

scala> freq("caterpillar")
res:
  scala.collection.mutable.HashMap[Char,Int] 
= Map(c -> 1, a -> 2, e -> 1, i -> 1,
      r -> 2, t -> 1, l -> 2, p -> 1)

or Lists

scala> freq(List(1,2,1,13,1,2,1,3,31,12,1))
res:
  scala.collection.mutable.HashMap[Int,Int] 
= Map(12 -> 1, 3 -> 1, 13 -> 1,
      1 -> 5, 2 -> 2, 31 -> 1)

or ...

Compiling and Running

Compiling and Running

To make an executable, first put the functions in a file,
e.g. scala-crash.scala:

object Freq { ... }

Compiling and Running

Compile with:

$ scalac scala-crash.scala

Run from command-line with:

$ scala Freq foo.txt
...
$ scala Freq bar.txt
...

Or (after compiling) run in the Scala REPL:

scala> val m = Freq("caterpillar")
scala> Freq.show(m, 1)
...

Loading Source Files in REPL

Another way to quickly load functions from a file:

scala> :load scala-crash.scala
scala> val m = Freq("caterpillar")
scala> Freq.show(m, 1)
e : #
t : #
p : #
a : ##
i : #
r : ##
l : ##
c : #

Code Completion in REPL

Handy way to discover APIs!

scala> "hello".<Hit Tab>
!=         ##        #asInstanceOf
+          ...       ...
charAt     ...       ...
...


scala> val l = List(1, 2)
scala> l.<Hit Tab>
...   filter  ...  foldLeft  ...  takeWhile   ...