Scala Basics

For in depth Scala learning check Programming in Scala written by Odersky, Martin; Spoon, Lex; Venners, Bill

First thing first, download and install Scala https://docs.scala-lang.org/getting-started/index.html

Let's start with some basics, Scala has two kinds of variables, vals and vars. A val is similar to a final variable in Java. Once initialized, a val can never be reassigned. A var, by contrast, is similar to a non-final variable in Java. A var can be reassigned throughout its lifetime. Here's a val definition: 

val msg = "Hello, world!"

Scala's inference capability allow us not to mention variable type, Scala compiler automatically will infer msg is String type based on assigned value "Hello world", still if you want to mention then you can:

val msg : String = "Hello, world!"

Scala functions can define using keyword def:

def max(x: Int, y: Int): String = {
if (x > y) "He Should " else "He should not"
}

Note Scala doesn't infer function parameters and you must mention variable type (x:Int, y:Int here). Return keyword is also not require, Scala consider last line as return statement [Scala Ifs are expressions which can return values, here either X or Y]. Based on return value Scala can infer return type, so String return type is optional here.

Similar to other languages Scala has imperative loops but they are more advance which we can see in Scala Loops in detail:

def factorial(x: BigInt): BigInt =
while (i < args.length) {
if (i != 0)
print(" ")
print(args(i))
i += 1
}
do {
println("Value of a: " + a);
a = a + 1;
} while (a < 20)
for (arg <- args)
println(arg)
// args.foreach((arg: String) => println(arg)) // prefer below line
/*
* If a function literal consists of one statement that takes a single argument,
* you need not explicitly name and specify the argument.
*/
args.foreach(println)


Scala list differ from Java in sense that scala.List is immutable, all the update methods actually returns new List. It's easy to create List in Scala:

 val myList = List(1,2,3)

4::myList // :: is call cons and it's a method in List to prepend a element.

Tuples can use to store different object or to return 2 or more different type of values :

 val pair = (99, "Luftballons",1.5) // three different data types
println(pair._1) // prints 99
println(pair._2)
println(pair._3)

Scala provides mutable and immutable collections with same name but in different packages namely scala.collection.mutable and scala.collection.immutable. Both the packages provide += method to add new elements but mutable collection returns new collection while immutable add new element ino existing collection:

import scala.collection.mutable

  

    val movieSet = mutable.Set("Hitch", "Poltergeist")

    movieSet += "Shrek"

    println(movieSet)

Even on the map += method works :

import scala.collection.mutable

  

    val treasureMap = mutable.Map[Int, String]()

    treasureMap += (1 -> "Go to island.")

    treasureMap += (2 -> "Find big X on ground.")

    treasureMap += (3 -> "Dig.")

    println(treasureMap(2))

when you say 1 -> "Go to island.", you are actually calling a method named -> on an integer with the value 1, passing in a string with the value "Go to island." This -> method, which you can invoke on any object in a Scala program, returns a two-element tuple containing the key and value. You then pass this tuple to the += method of the map object to which treasureMap refers.


Scala encourages you to do functional programming. if code contains any vars, it is probably in an imperative style. If the code contains no vars at all—i.e., it contains only vals—it is probably in a functional style. One way to move towards a functional style, therefore, is to try to program without vars. Another functional approach would be to return meaningful value from every method. If function is not returning values i.e., return type is Unit type then it will cause side effects. Try to minimise such side effects.


Classes & Objects:

If you don't put any modfier in front of class, method or variable then contray to Java it would be public in Scala. Public is Scala's default access level. For more details you can check https://www.geeksforgeeks.org/access-modifiers-in-scala/


Method parameters are by default or Vals not Vars, meaning you can't change them in method.


def doSomething () {

 // really do something and return result. 

}

In above method although the Scala compiler will correctly infer the result types of doSomething method, readers of the code will need to mentally infer the result types by studying the bodies of the methods. As a result it is often better to explicitly provide the result types of public methods declared in a class even when the compiler would infer it for you.


When a singleton object shares the same name with a class, it is called that class's companion object. You must define both the class and its companion object in the same source file. The class is called the companion class of the singleton object. A class and its companion object can access each other's private members. 


Java programmers can think companion object as home for all the stastics methods of a class.


One difference between classes and singleton objects is that singleton objects cannot take parameters, whereas classes can. Because you can't instantiate a singleton object with the new keyword, you have no way to pass parameters to it.


A singleton object that does not share the same name with a companion class is called a standalone object. You can use standalone objects for many purposes, including collecting related utility methods together or defining an entry point to a Scala application.


Singlton object with main method can be your application's entry point :

object Summer {

      def main(args: Array[String]) = {

        for (arg <- args)

          println(arg + ": " + calculate(arg))

      }

    }


Similar to other language you can import any package class in Scala class but Scala allow to put import statement anywhere in Scala file sothat you can put it near to relevant method. Scala implicitly imports members of packages java.lang and scala, as well as the members of a singleton object named Predef, into every Scala source file. Predef, which resides in package scala, contains many useful methods. For example, when you say println in a Scala source file, you're actually invoking println on Predef. (Predef.println turns around and invokes Console.println, which does the real work.) When you say assert, you're invoking Predef.assert.


As mentioned above in Scala every operator is a method so if you are doing 7*8 you are actually calling * method on 7 or in other words 7.*(8). Similar to this infix operator notations Scala also has prefix and postfix notations. In prefix notation, you put the method name before the object on which you are invoking the method (for example, the `-' in -7). In postfix notation, you put the method after the object (for example, the "toLong" in "7 toLong").


prefix and postfix operators are unary: they take just one operand. Some examples of prefix operators are -2.0, !found, and ~0xFF. As with the infix operators, these prefix operators are a shorthand way of invoking methods. In this case, however, the name of the method has "unary_" prepended to the operator character. For instance, Scala will transform the expression -2.0 into the method invocation "(2.0).unary_-". You can demonstrate this to yourself by typing the method call both via operator notation and explicitly: scala> -2.0                  // Scala invokes (2.0).unary_-

  res2: Double = -2.0

  

  scala> (2.0).unary_-

  res3: Double = -2.0

  

The only identifiers that can be used as prefix operators are +, -, !, and ~. Thus, if you define a method named unary_!, you could invoke that method on a value or variable of the appropriate type using prefix operator notation, such as !p. But if you define a method named unary_*, you wouldn't be able to use prefix operator notation because * isn't one of the four identifiers that can be used as prefix operators.

 

Postfix operators are methods that take no arguments, when they are invoked without a dot or parentheses. In Scala, you can leave off empty parentheses on method calls. The convention is that you include parentheses if the method has side effects, such as println(), but you can leave them off if the method has no side effects, such as toLowerCase invoked on a String:

scala> s toLowerCase

res5: String = hello, world!

In this case, without dot toLowerCase is used as a postfix operator on the operand s.


method calling is true even for logical operators , for example a&&b, c&d, 12<=15 and so on. You may be wondering how short-circuiting can work given operators are just methods. Normally, all arguments are evaluated before entering a method, so how can a method avoid evaluating its second argument? The answer is that all Scala methods have a facility for delaying the evaluation of their arguments, or even declining to evaluate them at all. The facility is called by-name parameters, we will discuss this later.


If you want to compare two objects for equality, you can use either == or its inverse !=.


scala> 1 == 2 

  res31: Boolean = false

  

  scala> 1 != 2 

  res32: Boolean = true

  

  scala> 2 == 2 

  res33: Boolean = true

 These operations actually apply to all objects, not just basic types. For example, you can use == to compare lists: scala> List(1, 2, 3) == List(1, 2, 3)

  res34: Boolean = true

  

  scala> List(1, 2, 3) == List(4, 5, 6)

  res35: Boolean = false

 Going further, you can compare two objects that have different types: scala> 1 == 1.0

  res36: Boolean = true


scala> List(1, 2, 3) == "hello"

  res37: Boolean = false

 You can even compare against null, or against things that might be null. No exception will be thrown: scala> List(1, 2, 3) == null

  res38: Boolean = false

  

  scala> null == List(1, 2, 3)

  res39: Boolean = false

 As you see, == has been carefully crafted so that you get just the equality comparison you want in most cases. This is accomplished with a very simple rule: First check the left side for null. If it is not null, call the equals method. Since equals is a method, the precise comparison you get depends on the type of the left-hand argument. Since there is an automatic null check, you do not have to do the check yourself.


For refrecne equality check, where you want to check whether two objects referring to same memory or not , you can use eq method. Opposit of this method is ne method.


Operator precedence:

you may be wondering how operator precedence works. Scala decides precedence based on the first character of the methods used in operator notation. If the method name starts with a *, for example, it will have a higher precedence than a method that starts with a +. Thus 2 + 2 * 7 will be evaluated as 2 + (2 * 7).


The one exception to the precedence rule, concerns assignment operators, which end in an equals character. If an operator ends in an equals character (=), and the operator is not one of the comparison operators <=, >=, ==, or !=, then the precedence of the operator is the same as that of simple assignment (=). That is, it is lower than the precedence of any other operator. For instance: x *= y + 1

 means the same as: x *= (y + 1)

 because *= is classified as an assignment operator whose precedence is lower than +, even though the operator's first character is *, which would suggest a precedence higher than +.


When multiple operators of the same precedence appear side by side in an expression, the associativity of the operators determines the way operators are grouped. The associativity of an operator in Scala is determined by its last character. Any method that ends in a `:' character is invoked on its right operand, passing in the left operand. Methods that end in any other character are the other way around: They are invoked on their left operand, passing in the right operand. So a * b yields a.*(b), but a ::: b yields b.:::(a). 


No matter what associativity an operator has, however, its operands are always evaluated left to right. So if a is an expression that is not just a simple reference to an immutable value, then a ::: b is more precisely treated as the following block: 

{ val x = a; b.:::(x) }

 In this block a is still evaluated before b, and then the result of this evaluation is passed as an operand to b's ::: method.


This associativity rule also plays a role when multiple operators of the same precedence appear side by side. If the methods end in `:', they are grouped right to left; otherwise, they are grouped left to right. For example, a ::: b ::: c is treated as a ::: (b ::: c). But a * b * c, by contrast, is treated as (a * b) * c.


Through implicit conversion Scala has provided wrapper classes (called Rich wrappers in scala.runtime package) for several additional methods:

Code  Result

0 max 5  5

0 min 5 0

-2.7 abs 2.7

-2.7 round -3L 

1.5 isInfinity false 

(1.0 / 0) isInfinity true 

4 to 6 Range(4, 5, 6) 

"bob" capitalize "Bob"

"robert" drop 2  "bert"


Implicit conversions used to add more functionality to value types. For instance, the type Int supports all of the operations like 42 max 43, 1 min 20, 1 to 6, 4 until 9 etc. how this works: The methods min, max, until, to, and abs are all defined in a class scala.runtime.RichInt, and there is an implicit conversion from class Int to RichInt. The conversion is applied whenever a method is invoked on an Int that is undefined in Int but defined in RichInt. Similar "booster classes" and implicit conversions exist for the other value classes as well.


You also can enrich your class by implicit method: https://medium.com/@lprakashv/making-ordinary-classes-rich-scala-ab7f991d690  


Scala's Hierarchy:

In Scala, every class inherits from a common superclass named Any and every class has Null and Nothing at bottom.

https://docs.scala-lang.org/tour/unified-types.html


The equality and inequality methods, == and !=, are declared final in class Any, so they cannot be overridden in subclasses but you can overirde equals method in your class to tell if two objects are equal or not. Scala will use your equals methods for == and != https://www.geeksforgeeks.org/object-equality-in-scala/


There are situations where you need reference equality instead of user-defined equality. For example, in some situations where efficiency is paramount, you would like to hash cons with some classes and compare their instances with reference equality.[3] For these cases, class AnyRef defines an additional eq method, which cannot be overridden and is implemented as reference equality (i.e., it behaves like == in Java for reference types). There's also the negation of eq, which is called ne.


Note that the value class space is flat; all value classes are subtypes of scala.AnyVal, but they do not subclass each other. Instead there are implicit conversions between different value class types. For example, an instance of class scala.Int is automatically widened (by an implicit conversion) to an instance of class scala.Long when required.


If you see the Scala hirarchi then you will find Null & Nothing at the bottom. null can assign to any reference but not compatible with value types (e.g., Int). Type Nothing has no value of it's type, Nothing uses to signal for abnormal termination. 

For example:

def divide(x: Int, y: Int): Int = 

    if (y != 0) x / y 

    else sys.error("can't divide by zero") //sys.error has retrun type Nothing and Nothing is a subtype of Int, the type of the whole conditional is Int, as required.


Defining your own value classes :

You can define your own value class like :

class Dollars(val amount: Int) extends AnyVal { // val allows the amount parameter to be access as a field

    override def toString() = "$" + amount

}

For a class to be a value class, it must have exactly one parameter and it must have nothing inside it except defs. Furthermore, no other class can extend a value class, and a value class cannot redefine equals or hashCode.


The benefit of value class is, it is of type Dollars in Scala source code, but the compiled Java bytecode will use type Int directly.


val money = new Dollars(1000000)

println(money) // Scala It will print $1000000

println()


Defining such tiny classes is a way to help the compiler be helpful to you. For example suppose you are writing some code to generate HTML. In HTML, a style name is represented as a string. So are anchor identifiers. HTML itself is also a string, so if you wanted, you could define helper code using strings to represent all of these things, like this: 


def title(text: String, anchor: String, style: String): String =  s"<a id='$anchor'><h1 class='$style'>$text</h1></a>"


Problme with this is there is no check whether first argulemnt is text or not, user can pass anchor or style with it ! To avoid such situation it's advisable to write tiny classes:

  class Anchor(val value: String) extends AnyVal

  class Style(val value: String) extends AnyVal

  class Text(val value: String) extends AnyVal

  class Html(val value: String) extends AnyVal


Now user can not do mistake:

def title(text: Text, anchor: Anchor, style: Style): Html =

    new Html(

      s"<a id='${anchor.value}'>" +

          s"<h1 class='${style.value}'>" +

          text.value +

          "</h1></a>"

    )