Prasanna Natarajan

Learning Scala - Anki notes

I went through the Coursera course to learn scala from the language’s creator Martin Odersky. Here are the anki notes I took while learning.


define a fn that takes

  • an int,
  • a function that takes and returns int, and
  • a fn that only returns int.
def foo(x: Int, y: Int => Int, z: => Int): Int =
  y(x) + z

//fn that takes no arg but returns a val.
//Actually it's just a val or expression, not a fn
def z(): Int = 42

foo(2, x => x*x, z)

def and(x: Boolean, y: Boolean): Boolean =
  if (x) y else false

def loop(): Boolean = loop

and(false, loop)

What does the last line return?
If it’s a bug, what’s the fix?

Ideally it should return false because we are short-circuiting.
But loop is evaluated by ‘call-by-value’ strategy and so gets into loop.
So make y a call-by-name thingy.

def and(x: Boolean, y: => Boolean): Boolean = // y is now a fn that returns Bool
  if (x) y else false

What’s tail recursion and write the TR version of this factorial fn:

def fact(x: Int): Int =
  if (x == 0) 1 else x * fact(x - 1)
def fact1(x: Int): Int = {
  def loop(acc: Int, x: Int): Int =
    if (x == 0) acc
    else loop(acc * x, x-1)

  loop(1, x) // initial call
}

Some string and char operations

  • convert string to list of chars
  • check if list is empty
  • get the first elem of the list
  • get the tail of the list (except the first elem)

  • convert string to list of chars - “prasanna”.toList
  • check if list is empty - list.isEmpty
  • get the first elem of the list - list.head
  • get the tail of the list (except the first elem) - list.tail

how to write this fn in currying style:

def sum(f: Int => Int, a: Int, b: Int): Int = {
  if (a > b) 0
  else f(a) + sum(f, a+1, b)
}

Also what’s the curried fn’s type? (the elm way)

// The curry style.
// the curried fn's type is - not int, not a fn. It's like this:
// (Int => Int) => (Int, Int) => Int
def sumC(f: Int => Int)(a: Int, b: Int): Int = {
  if (a > b) 0
  else f(a) + sumC(f)(a+1, b)
}

sumC(x=>x)(1, 10) // 55

Note that this is not curry style:

def sumF(f: Int => Int): (Int, Int) => Int = {
  def sum(a: Int, b: Int): Int = {
    if (a>b) 0
    else f(a) + sum(a+1, b)
  }
  sum
}

val sq = sumF(x => x)
sq(1, 10) // 55

write 3 fns:

  • product of f(x) of all nums between a and b
  • recursive factorial fn
  • factorial using the product fn
def product(f: Int => Int)(a: Int, b: Int): Int = {
  if (a>b) 1
  else f(a) * product(f)(a+1, b)
}

product(x=>x)(1, 5)

def fact(x: Int): Int = {
  if (x<=1) 1
  else x * fact(x-1)
}
fact(5)

def factProduct(x: Int): Int = product(x=>x)(1, x)
factProduct(5)

Give example of pattern matching with string.

Here’s the fn def:

def response(statement: String): String  
object Bob {  
def response(statement: String): String = statement.trim match {  
case s if s.isEmpty => "Fine. Be that way!"  
case s if shouting(s) && questioning(s) => "Calm down, I know what I'm doing!"  
case s if shouting(s) => "Whoa, chill out!"  
case s if questioning(s) => "Sure."  
case _ => "Whatever."  
}  

def shouting(statement: String): Boolean = {  
statement.toUpperCase == statement && hasLetters(statement)  
}  

def questioning(statement: String): Boolean = {  
statement.takeRight(1) == "?"  
}  

def hasLetters(statement: String): Boolean = {  
statement.exists((('a' to 'z') ++ ('A' to 'Z')).toSet.contains(_))  
}  
}  

what are abstract classes?

classes that just have defs, with or without body.
can’t be instantiated.

Can only be extended by another class.
methods can be overridden.

abstract class Base {
  def foo = 42
  def bar = 42
}

class Child extends Base {
   override def bar = 43
}

val c = new Child
c.foo // 42
c.bar // 43

package and import:

How to place a class/object within a package.
How to import?

Place in package:

package foo.bar

object A {...}
class B {...}

import:

import foo.bar.A
import foo.bar.{A, B} // import specifics
import foo.bar._ // import all

what are type aliases?

If a set is represented a fn that takes an elem and says if the elem contains it, then define a type Set.

And then define a contains fn that takes a set and an int and returns bool.

to give meaningful types based on domain.


type Set = Int => Boolean

def contains(s: Set, elem: Int): Boolean = s(elem)

This fn takes an int and returns a List of that int.
How to make it generic for all types - char, string etc?

def toArray(a: Int): List[Int] =
  List(a)
def toArray[T](a: T): List[T] =
  List(a)

toArray[Int](1)
toArray[Char]('a')
toArray[String]("pras")

Type upper bound. What’s it and how to define it?

abstract class Animal {
 def name: String
}

abstract class Pet extends Animal {}

class Cat extends Pet {
  override def name: String = "Cat"
}

class Lion extends Animal {
  override def name: String = "Lion"
}

Define a pet container. Lions aren’t allowed.

// we say the type of PetContainer is P and that
// it should be a subtype of Pet.
// (Pet is the upperbound).

class PetContainer[P <: Pet](p: P) {
  def pet: P = p
}
val dogContainer = new PetContainer[Dog](new Dog) // works

val lionContainer = new PetContainer[Lion](new Lion) // doesn't compile

covariant
contravariant
nonvariant

Explain covariant type.

Here the drinks classification:

drink => softdrink => (cola, mocktail)
=> harddrink => (beer, whiskey)

And lets say our office has agreed to put up a softdrink vendingmachine.

Make that vendingmachine a covariant type for softdrink type,
so that harddrinks are not allowed.

abstract class Drink {
def name: String
}

abstract class SoftDrink extends Drink {}
abstract class HardDrink extends Drink {}

////

class Cola extends SoftDrink {
override def name: String = “Cola”
}

class Mocktail extends SoftDrink {
override def name: String = “Mocktail”
}

//

class Beer extends HardDrink {
override def name: String = “Beer”
}

class Whiskey extends HardDrink {
override def name: String = “Whiskey”
}

And here’s the vendingmachine def:

def install(vm: VendingMachine[SoftDrink]): Unit =
  println(s"Installing $vm")

// invariant
install(new VendingMachine[SoftDrink])
// covariant
install(new VendingMachine[Cola])
// doesn't compile
install(new VendingMachine[Beer])
What should the vm def be for the above "doesn't compile" to be true?

// VM takes any subtype of the passed in class 'SoftDrink'

class VendingMachine[+A]


---

Difference betwee upper bound and covariant types?

C[A <: B]  => class C takes type A, which should be a subtype of type B
vs
C[+B] =>class C  takes type X, which can be anything, but it should be a subtype of B

(wtf?)

```scala
class Animal {}
class Dog extends Animal {}

class Car {}
class SportsCar extends Car {}

// covariance

case class Listx[+B](elements: B*) {}

val animals: Listx[Animal] = Listx( new Dog(), new Animal() )
val cars: Listx[Car] = Listx ( new Car(), new SportsCar() )

//

case class Shelter(animals: Listx[Animal]) {}

val animalShelter: Shelter = Shelter( Listx(new Animal()): Listx[Animal] )
val dogShelter: Shelter = Shelter( Listx(new Dog()): Listx[Dog] )

// upper bound

case class Barn[A <: Animal](animals: A*) {}

val animalBarn: Barn[Animal] = Barn( new Dog(), new Animal() )

// doesn't compile.
//val carBarn = Barn( new SportsCar() )

Create this list using the cons operator.
And what’s the actual form of it?

List(2, 3, 4)

// all are same

List(2, 3, 4)
2::3::4::Nil
Nil.::(4).::(3).::(2)

what’s Nil wrt List?

How is it used to create a list?

How is a list represented? (Martin’s teaching)

What are a list’s 3 basic common operations that all others depend upon?

List() matches to Nil. But Nil is not equal to List().

Using cons operator, to create a list:

1::2::3::Nil  // List(1, 2, 3)

Nil.::(3).::(2).::(1) // List(1, 2, 3)

Martin says: a list as linked list.
first box has first elem and a reference to the next box.
The last box has the last elem and then Nil.

3 basic ops: head, tail, isEmpty


How lists are pattern matched?

ie, what Lists do these patterns match?

Nil
List()
x :: Nil
x :: y (bonus, what's the guaranteed length of this list? 1 or 2 or >1 or >2)
List(2 :: x)
Nil => List()
List() => List()
x :: Nil => List(1)
x :: y 
(bonus, what's the guaranteed length of this list? 1 or 2 or >1 or >2) => 
List(1, 2, 3, 4)  (min length is >=1.
y could be empty list too)
List(2 :: x) => List(List(2, 3, 4))  (here x is List(3, 4))

How to deal with a function that can return a specific type or Null?

Normally you should check the fn’s documentation and while calling the fn,
be prepared to handle Null.
But if you forget, the bug might surprise you later.

In Scala, these optional values can be wrapped into an Option instance.

val name: Option[String] = Some("pras") // or None  
val upper = name map { _.trim } filter { _.length != 0 } map { _.toUpperCase }  
println(upper getOrElse "nope") // this is one way to get the actual value from Some  

pattern matching can also be used:

val nameMaybe: Option[String] = request getParameter "name"
nameMaybe match {
  case Some(name) => println(name.trim.toUppercase)
  case None => println("No name value")
}  

Given these:

val l = List(11, 22, 33)  
val m = List(44, 55, 66)  

wtf are these?

l ++ m  
l ::: m  
l :: m  
// both does same: concats 2 lists into 1 list.
// List(11, 22, 33, 44, 55, 66)
// prefer ++  
l ++ m  
l ::: m  

// Here, m is a list to which elem l is prepended.
// m.prepend(l) (in ruby)
// m.::(l) (in scala)
// List(List(11, 22, 33), 44, 55, 66)  
l :: m  

vectors vs lists

prepend/append 77 to list/vector

val list = List(11, 22, 33)  
val vector = List(11, 22, 33)  

val list = List(11, 22, 33)  

77 :: list // prpnd  
list :+ 77 // appnd  

val vector = List(11, 22, 33)  

77 :: vector // prpnd  
77 +: vector // prpnd  
vector :+ 77 // appnd  

Given an int ‘n’,
and ‘i’ and ‘j’ as 1 <= j < i < n,
and def isPrime(n: Int): Boolean = (2 until n) forall (n % _ != 0)

Find all pairs of (i, j) where i+j is a prime.

Here’s the regular version.

(1 until n) flatMap (i =>
  (1 until i) map (j => (i, j))) filter (pair =>
    isPrime(pair._1 + pair._2))

Write the:

  • case expression version
  • for-expression version
(1 until n) flatMap (i =>
  (1 until i) map (j => (i, j))) filter {case(x, y) => isPrime(x+y)}

for (i <- 1 until n; j <- 1 until i if isPrime(i+j)) yield (i, j)

for {
  i <- 1 until n
  j <- 1 until i
  if isPrime(i+j)
} yield (i, j)

What’s x?

val x = 'r' -> 42

What’s it wrt this Map: val m = Map('a' -> 11, 'b' -> 22, 'c' -> 33)

Transform this map like so:

  • keys become uppercase,
  • vals become val+1

x is just a Tuple2 instance: (r, 45)
The key -> value is just syntax sugar.

So maps are basically represented with tuples:
val m = Map(('a', 11), ('b', 22), ('c', 33))

def transformer(t: (Char, Int)) = {
  t._1.toUpper -> (t._2 + 1) // or (t._1.toUpper, t._2 + 1)
}

m map transformer // Map(A -> 12, B -> 23, C -> 34)

implement method_missing in scala!

refactor this:

object SpaceAge {
  def onEarth(age: Double): Double = age / yearToSecs(1)

  def onMercury(age: Double): Double = age / yearToSecs(0.2408467)

  def onVenus(age: Double): Double = age / yearToSecs(0.61519726)

  def yearToSecs(year: Double): Double = year * 31557600
}
import scala.language.dynamics

object spaceAge extends Dynamic {
  val earthYears: Map[String, Double] = Map(
    "Earth" -> 1,
    "Mercury" -> 0.2408467,
    "Venus" -> 0.61519726)

  def yearToSecs(year: Double): Double = year * 31557600

  def applyDynamic(methName: String)(age: Double): Double = {
    val planet = methName.substring(2)
    age / yearToSecs(earthYears(planet))
  }

}

method_missing using Dynamic trait.

How to implement various methods:
getter, setter, getter with args, setter with args etc

Check https://www.scala-lang.org/api/2.12.x/scala/Dynamic.html

In your obj/class that extends Dynamic,
you have to implement the methods on the RHS below.

foo.method("blah")      ~~> foo.applyDynamic("method")("blah")
foo.method(x = "blah")  ~~> foo.applyDynamicNamed("method")(("x", "blah"))
foo.method(x = 1, 2)    ~~> foo.applyDynamicNamed("method")(("x", 1), ("", 2))
foo.field           ~~> foo.selectDynamic("field")
foo.varia = 10      ~~> foo.updateDynamic("varia")(10)
foo.arr(10) = 13    ~~> foo.selectDynamic("arr").update(10, 13)
foo.arr(10)         ~~> foo.applyDynamic("arr")(10)

What are partial functions PF?

2 ways to define them?
explicitly defining apply and isDefinedAt vs using case stmt

usecases wrt collection’s ‘map’ method.

PartialFunction trait’s 2 useful methods for chaining?

A PF doesn’t return result for all values of its input.
It defines what input it can answer.
It can be queried with a specific input and see if it can answer.

2 ways to define:

val doubleEvens: PartialFunction[Int, Int] = {  
case x if x % 2 == 0 => x * 2  
}  

val tripleOdds = new PartialFunction[Int, Int] {  
def apply(x: Int): Int = if (x%2 != 0) x * 3 else throw new Error("I don't know")  
override def isDefinedAt(x: Int): Boolean = (x % 2) != 0  
}  

doubleEvens(2) // 4  
tripleOdds(5) // 15  

val sample = 1 to 10  

//sample map tripleOdds  THIS FAILS

// This succeeds bcos collect accepts partialFn
// returns Vector(3, 9, 15, 21, 27)  
sample collect tripleOdds  

Given this:

val add2 = (x: Int) => x + 2
val triple = (x: Int) => x * 3

val a = 2

Write this using compose and andThen:

triple(add2(a))  // 12
add2(triple(a)) // 8

What’s the ruby way to do this?

// these are same
triple(add2(a))
(add2 andThen triple)(a)
(triple compose add2)(a)

// these are same
add2(triple(a))
(triple andThen add2)(a)
(add2 compose triple)(a)

You can’t even do this in ruby:

add2 = ->(x) { x + 2 }
triple = ->(x) { x * 3 }

a = 2

p triple[add2[a]]
// so ugly
p add2[a].yield_self {|s| triple[s]}

pattern matching,
partial functions,
case statements without “x match …” prefix.

use these together to make this work:

val partial = one orElse two orElse three orElse wildcard
partial(5) // returns "something else"
val one: PartialFunction[Int, String] = { case 1 => "one" }
val two: PartialFunction[Int, String] = { case 2 => "two" }
val three: PartialFunction[Int, String] = { case 3 => "three" }
val wildcard: PartialFunction[Int, String] = { case _ => "something else" }

// now you can compose a 'partial' fn like this:
val partial = one orElse two orElse three orElse wildcard

Writing queries using for:

Given a set of books like this:

case class Book(title: String, authors: List[String])  

val books: Set[Book] = Set(  
Book("how to fail", List("Adams Scott")),  
Book("god's debris", List("Adams Scott")),  
Book("dilbert principle", List("Adams Scott")),  
Book("insight meditation", List("Goldstein Joseph")),  
Book("4hr workweek", List("Ferriss Tim"))  
)  
  • find titles of books whose author’s name contains ‘scott’
  • return authors who have written at least 2 books
for {  
b <- books  
a <- b.authors  
if a.contains("Scott")  
} yield b.title  

for {  
b1 <- books  
b2 <- books  
if b1.title < b2.title  
a1 <- b1.authors  
a2 <- b2.authors  
if a1 == a2  
} yield a1