ScalaTuts

From Zero to Hero in Scala

Abstract Members

A member of a class or trait is abstract if the member does not have a com- plete definition in the class. Abstract members are intended to be imple- mented in subclasses of the class in which they are declared.

In scala you can also declare abstract fields and even abstract types as members of classes and traits.

The following trait declares one of each kind of abstract member: an abstract type (T), method (transform), val (initial), and var (current):

1
2
3
4
5
6
7
8
9
10
11
12
13
trait Abstract {
    type T
    def transform(x: T): T
    val initial: T
    var current: T
}

class Concrete extends Abstract {
    type T = String
    def transform(x: String) = x + x
    val initial = "hi"
    var current = initial
      }

As you can see from the example in the previous section, the term abstract type in Scala means a type declared (with the “type” keyword) to be a mem- ber of a class or trait, without specifying a definition. Classes themselves may be abstract, and traits are by definition abstract, but neither of these are what are referred to as abstract types in Scala. An abstract type in Scala is always a member of some class or trait, such as type T in trait Abstract.

Type Parameterization

Type parameterization allows you to write generic classes and traits.Sets are generics hence we can define a set of Integer or a set of Strings like Set[Int] or Set[String]

How to hide a constructor

In java we can hide the constructor by making the constructor private where as in scala we can hide the constructor by private modifier in front of the class parameters.

1
2
3
4
5
6
7
8
9
10
11
12
13
class AccessibleConstructor (id : String){
}
scala> val x = new AccessibleConstructor("hello")
x: AccessibleConstructor = AccessibleConstructor@7485fef2

The above works.

class HideConstructor private(id : String){
}
scala> val hide = new HideConstructor("hidden")
<console>:17: error: constructor HideConstructor in class HideConstructor
    cannot be accessed in object $iw
    val hide = new HideConstructor("hidden")

Collections

Iterable v/s Iterator

  • Iterable represents a collection of items over which we can iterate.
  • Iterator is a mechanism through which we iterate.

Iterable can be use to re-iterate over the collection of items whereas iterator cannot be reused.

ListBuffer and ArrayBuffer

ListBuffer and ArrayBuffer are mutable types similar to List and Array. They provide methods for constant time prepending and appending data to the buffers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
scala> val buf = new ListBuffer[Int]
buf: scala.collection.mutable.ListBuffer[Int] = ListBuffer()


scala> buf += 1
res0: buf.type = ListBuffer(1)

scala> buf += 2
res1: buf.type = ListBuffer(1, 2)

scala> 3 +=: buf
res2: buf.type = ListBuffer(3, 1, 2)

scala> buf.toList
res3: List[Int] = List(3, 1, 2)

Scala Lists Part 2

Higher order methods allow us to iterate over the list elements and transform elements.

  • map This method takes a function f and returns a list by applying the function f on each element in the list. The example below double each element in the array and returns a new array.
1
2
3
4
5
6
7
8
scala> val arr = List(1,2,3)
arr: List[Int] = List(1, 2, 3)

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

scala> arr map (2*_)
res1: List[Int] = List(2, 4, 6)
  • flatMap flatMap takes a function returning a list and return a list by flattening.
1
2
3
4
5
scala> val words = List("Scala","Tuts")
words: List[String] = List(Scala, Tuts)

scala> words.flatMap(_.toList)
res7: List[Char] = List(S, c, a, l, a, T, u, t, s)

Scala Lists

List Properties

Lets first look at the properties of Lists.

  • Lists are immutable (List elements can’t be changed by assignment).
  • Lists are homogenous
  • List type is Covariant

Covariant means that for each pair of types S and T, if S is a subtype of T, then List[S] is a subtype of List[T].

First order List Methods

Concatenation of List

List can be concatenated using ::: operator. It takes two List as arguments and return the concatenated List as the result

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

Reversing a List

In this example we will see how we can reverse a list. The purpose of this is example is just to see how we can use pattern List concatenation and pattern matching to reverse a list.

1
2
3
4
5
6
7
8
9
10
scala> val arr = List(1,2,3,4,5)
arr: List[Int] = List(1, 2, 3, 4, 5)    
scala> def rev(x:List[Int]):List[Int] = x match {
          case List() => x
          case x :: xs => rev(xs):::List(x)
         }
rev: (x: List[Int])List[Int]

scala> rev(arr)
res0: List[Int] = List(5, 4, 3, 2, 1)

Scala Pattern Matching - Part 2

Option Type

Scala has a special standard type Option for optional values. Option can have two values Some(x) which refers to value x or None which is indicative of nothing. Scala collections often tend to return Options. For example Map return Some(x) when the key is present or None when it is not.

1
2
3
4
5
6
7
8
9
scala> val users =
       Map("John" -> "Doe", "Steve" -> "Jobs")
users:
scala.collection.immutable.Map[java.lang.String,
java.lang.String] = Map(John -> Doe, Steve -> Jobs)
scala> users get "John"
res21: Option[java.lang.String] = Some(Doe)
scala> users get "Java"
res22: Option[java.lang.String] = None
1
2
3
4
5
6
7
8
9
10
11
scala> def show(x: Option[String]) = x match {
       case Some(s) => s
       case None => "?"
     }
show: (Option[String])String
scala> show(users get "John")
res23: String = Doe
scala> show(users get "Steve")
res24: String = Jobs
scala> show(users get "Java")
res25: String = ?

Scala Pattern Matching

Benefits of Case Classes

  • Case Classes help in pattern matching
  • Case Classes add Factory method with the class name. So we can create an object
1
2
3
4
case class TestCase(x){

}
val data = TestCase(x)
  • All the arguments are given val prefix.
  • Compiler adds the natural implementation of toString,hashCode and equals.

Note : These syntactic sugar comes with a price. Adding case to the class definition will make the objects bigger. Thats because methods and vals are added automatically.

Traits

Traits in Scala are similar to interfaces in Java but not completely . Traits encapsulates both method and field definitions where as in Java Interfaces can only contain method definitions and not fields. Also we can mix multiple traits into a class definition.

1
2
3
4
5
trait Notifier {
  def getNotified()  {
    println("Notified do some job.")
  }
}

Also one key difference with Java interfaces is that in Traits you can provide definitions for some of the functions which is not possible in Java. Traits also defines a type.

1
2
3
4
5
6
7
8
9
10
11
class MemoryCache extends Notifier{
    override def toString = "A Class for handling expired keys"
}
scala> val cache = new MemoryCache
cache: MemoryCache = A Class for handling expired keys

scala> val memorycache:MemoryCache = cache
memorycache: MemoryCache = A Class for handling expired keys

scala> memorycache.getNotified()
Notified do some job.

If we want to mix a trait which explicity extends a superclass , we can use the “with” keyword to do that

1
2
3
class Memtest extends CustomTest with Mytrait{

}    

Since traits can have concrete methods they can be used to provide rich interfaces.

Compressed Cache

Exploring Compressed Cache

To understand OO in scala lets design a compressed cache component which will provide compression for cache and support different storage backend for the compressed content.

To make is simpler we will create a abstract class CompressedCache which will define the compression and decompression functionality which are independent of the storage backend which will be used for the cache. There are two abstract method which are storeKV and getKV which will need to be implemented by the extending class since the way we store and retrieve depend on the backend.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* Abstract class for the compressed cache
 * Provides
 */
abstract class CompressedCache {
    def compress(data : String):Array[Byte] = {
      /* Compress the data */

      val compressed_data = "Compress logic here"
      compressed_data.toCharArray.map(_.toByte)
    }

    def decompress(compressed_data : Array[Byte]): String = {

      /* Decompress data logic*/
      val decompressed_data = " Decompress logic here"
      decompressed_data.toString
    }

    /* Abstract method declaration for storeKV & getKV, the extended class
     * should define the method
     */
    def storeKV(key : String, value : String)

    def getKV(key : String, value : String)
}

Lets go ahead and implement different compressed caches.

OO in Scala

Composition

Composition means one class holds a reference to another class to perform of complete certain operations.

Inheritance

In this type once class extends another class to provide an extended functionality or implement missing functionality as declared by the abstract class.

Abstract Class

Abstract class is a type class where the method is declared along with the parameters but definition is missing and the class extending the abstract class provide their own method definitions. Lets have a abstract class shape which has a abstract method getArea which would return the area of the shape. Along with that we will create two concrete classes square and circle and each shape implements their own getArea function which will return the area as described below.

1
2
3
4
5
6
7
8
9
10
11
12
13
abstract class shape {
    def getArea(): Double
}

class square  extends shape(width:Int){
    val area = width * width
    def getArea() = area
}

class circle extends shape(radius:Int){
    val area = 3.142*radius*radius
    def getArea() = area
}