Basics

How to declare a variable

Declare a constant variable

val x: Int = 42

or

val x = 42
  • types are optional
  • val cannot be reassigned, val are constant/immutable
  • Semicolon are not necessary

Declare a variable

var x: Int = 42

or

var x = 42

Expressions & Instructions

  • Changing a variable is called as side effects
  • Instructions are doing something.
  • Expression is something that has a value.
  • Donot write loops in Scala. Everything in Scala is an expression.
  • There is a special data type in Scala that is Unit. Unit is something like inline function. eg val x = while(i<10){println(i);i++}
  • Instructions are executed (Java). Expressions are evaluated (Scala).

Code Blocks

val codeBlock = {   val z = 2;   val w = z + 3;   if (w > 3) "hello" else "goodbye" } 
  • Code block is an expression.
  • The value of the whole block is the value of its last expression.

Functions

How to define functions

def aFunction(a: String, b: Int): String = {
    a + " " + b
}
def aFunction1(a: String, b: Int): String = a + " " + b
def aFunction2(a: String, b: Int): String = {
   if (b == 1) a
   else a + aFunction2(a, b - 1) 
}
  • An expression or a code block.
  • Parameterless functions can be called just by their names (ie. without using parenthesis ())
  • In Scala when you need loops, use recursions. This is a fundamental idea of functional programming.
  • The compiler can infer the return type of a function, but we cannot figure out the return type of a recursive function. As best practice always specify the return type.
  • You can also return Unit.

Interpolators

def play(name: String, age: Int) = println(s"Hi my name is $name and my age is $age")
def main(args: Array[String]): Unit = {
    play("Ashish", 31)
}

Tail Recursion (@tailrec)

Write recursion in the last expression. The idea used by compilers to optimize tail-recursive functions is simple, since the recursive call is the last statement, there is nothing left to do in the current function, so saving the current function’s stack frame is of no use.

object Playground {
    def concat(str: String, n: Int): String = {
        @tailrec
        def concat(str: String, word: String, n: Int): String = {
            if (n == 1) str
            else concat(str + word, word, n - 1);
        }
        concat(str, str, n);
    }
    def main(args: Array[String]): Unit = {
        println(concat("Ashish", 5))
    }
}

Call by name and call by value

  • In call by value, the expression is computed before passed to the function.
  • In call by name, the expression is passed as it is and is calculated every time it is being used.
  • Call by name can be used for lazy evaluations.
object Playground {
    def callByValue(n: Long) = {println(n);println(n)}
    def callByName(n: => Long) = {println(n);println(n)}
    def main(args: Array[String]): Unit = {
        callByValue(System.nanoTime())
        /* Output -
            10957615477957
            10957615477957
        */
        callByName(System.nanoTime())
        /* Output -
            10957771851376
            10957771908322
        */
    }
}

Default Parameters

Scala allows us to specify default value of a parameter. This is helpful in case of accumulators (tail recursion).

def fact(n: Int, acc: Int = 1): Int
Named Parameter
def fact(n: Int, acc: Int = 1): Int
fact(acc = 12, n = 10);

Object Oriented

Obect Oriented Basic

Syntactic Sugar: Method Notations

  • In Scala All operators are methods
  • In Scala every unary operators are methods
  • Functions which doesn’t have any parameter can be used as postfix unary operators.
  • There is a special method called as apply()
import scala.language.postfixOps

class Person {
    //prefix
    def unary_! = "This is a string"
    //postfix
    def isAlive: Boolean = true
    //directly calling object()
    def apply(): String = "Hi buddy"
}

@main
def main(): Unit = {
    val marry = new Person
    //prefix
    println(!marry)
    //postfix
    println(marry isAlive)
    //effect of apply method
    println(marry())
}
import scala.language.postfixOps

class Person(val name: String, var age: Int = 0) {
    def unary_! = "This is a string"
    def +(postfix: String): Person = new Person(s"${this.name} is a ${postfix}")
    def ++ = age = age + 1;
    def learns(str:String):String = s"${this.name} learns ${str}"
    def apply(num: Int): String = s"${this.name} watched inception ${num} times"
}

@main
def main(): Unit = {
    val ashish = new Person("Ashish")
    ashish++;
    println(ashish.age)
    println(ashish learns "Scala")
    println(ashish(2))
}

Scala Objects

  • Scala doesn’t have the concept of static (class level functionality). The alternative is declare as object
  • Scala object is like a singleton instance
object Person {
    var totalPerson: Integer
}

Scala Applications are either declared using main or by extending App

object Person extends App {
    var counter: Integer = 0
    val ashish = new Person("Ashish")
    println(ashish.age)
    println(ashish learns "Scala")
    println(ashish(2))
}
@main
def main(): Unit = {
    val ashish = new Person("Ashish")
    println(ashish.age)
    println(ashish learns "Scala")
    println(ashish(2))
}

Overriding & Inheritence

  • use keyword override
  • using sealed keyword in class definition, we can allow a class to get extended in the same file only.

Abstract classes vs traits

  • Abstract classes can have both abstract and non abstract methods
  • A trait is like an interface with a partial implementation. In scala, trait is a collection of abstract and non-abstract methods. You can create trait that can have all abstract methods or some abstract and some non-abstract methods.
  • traits donot have constructor parameters
  • traits can be extended using with
  • Can extend only one abstract class but we can extend multiple traits

Generics

  • In Scala generics are defined using [T] ie. square brackets

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *