Just some random notes about scala that you would notice as you start learning scala ( this is fairly long post )
scala support first class functions – this means scala treats functions just like variables – you can pass it to another function as an argument
it supports closures – A closure is a function, whose return value depends on the value of one or more variables declared outside this function
it supports currying – which is where a funtion that takes multiple arguements can be written as if it takes one argument and then a chain of calls to bring the same functionality as earlier , this makes it easier to pass the function as a first class function.
it supports pattern matching – case classes
Scala has a strong type inference , so its statically typed but we can leverage the type inference
scala> var radi = 10
radi: Int = 10
scala> var raii = 10.0
raii: Double = 10.0
in the case below , scala inferred the types based on the values . The compiler is smart enough to check the type
scala> var radi:Int = 10.0
^
error: type mismatch;
found : Double(10.0)
required: Int
use triple quotes “””” for multiline strings. “””
you can use == operator to compare strings
String interpolation is supported , so scala will substitute the variable in the string thats delimited by $. need a ‘s’ at the beginning of the string
s” hello $valword”
the $valword will be replaced by whatever value it has . you can use formula with {} . for printf , precede the string with a ‘f’ and then the format with the % command
scala has a unified type system, java has the value and reference type , primitives like integer etc are passed by value whereas String , collections etc are object and passed by reference.
everything is an object in scala – no primitives , no wrapper types etc – functions are objects too – first class function
Scala solves this conundrum by declaring superclass – two superclass Anyval and AnyRef and then these have another class called Any …see this for better idea –https://docs.scala-lang.org/resources/images/tour/unified-types-diagram.svg
null in java and scala are the same , it can be used as a value for a reference type but not for value type . Null is a trait ie a type and not a value , null is a type of Null. Nothing is also a trait . Nothing can never be instantiated but Nothing extends everything because its a subtype of both AnyVal and AnyRef. Nil is a special value associated with an empty list. Nil is the singleton instance of List[Nothing]. List are singly linked list in scala and Nil is what the last element points to. None is a special value associated with an option , so if the function does return a value sometimes…. great , if not it can return None. Unit is similar to void in java as it is the return type of a function that returns nothing for instance
type operations -> asInstanceOf – similar to cast in java , however its better to use to<Type> e.g toLong – its class specific , and calls the system converter and its better .
you can use boolean method isInstanceOf[class] to check the type . you can use getClass to get the actual class
scala can be compiled or interpreted (remember the repl loop in intellij )
You can directly create a scala object as opposed to a class
A scala object corresponds to a static class in java
So object o creates a singleton object 0 as the instance of some anonymous class , it can be used to hold static members that are not associated with instances of some class
main is the entry point just like java , but the same can be achieved by extending App which is a trait.
Object o extends T makes the object 0 an instance of Trait T ; you can then pass o anywhere , a T is expected.
Traits in Scala are like partially implemented interfaces. It may contain abstract and non-abstract methods. It may be that all methods are abstract, but it should have at least one abstract method. Not only are they similar to Java interfaces, Scala compiles them into those with corresponding implementation classes holding any methods implemented in the traits.
You can say that using Scala trait, we can share interfaces and fields between classes. Within trait, we can declare variables and values. When we do not initialize them, they are abstract. In other cases, the implementing class for the trait internally implements them.
Unlike Java , Traits can be extended by an instance of the class and not just by the class itself. you can extend multiple traits thus giving the impression of multiple inheritance
val is immutable , this keeps the code safe for concurrent distributed applications. Var is mutable , better to use Val instead .
Don’t use the return keyword in scala , the last value is the return value in scala code , so it automatically picks it up …using return will allow you to compile the code , but its not advised.
Here is a good blog describing why we should not use return
https://tpolecat.github.io/2014/05/09/return.html
in scala – you either have a statement ( which does not return anything ) or expression ( which does return something ). A lot of stuff in java thats considered statement in java is considered as expression in scala . You can chain multiple expressions in an expression block thats enclosed in curly brackets – {} . The return type is whats the last expression returns within an expression block.
Scala does allow access to variables defined outside of the expression block ,this is what enables closure. You can have nested expression blocks and in cases of a var defined with the same name and runs into conflict the one closest in the scope is picked , this applies to return – the one that’s most inside defines the return type of a nested expression block
if/else is an expression in scala and if there is no else ( pun intended ) then the return value is Nothing , this is why type inference could infer the value to be Any.
match expressions are more common in scala than nested if else or switch-case in java, this is because match expression can match on value , type etc . match with case for each condition gives a way to conditionally initialize a val.
you can use OR ( syntax is the pipe symbol – ” | ” ) or patternGurad thats a if expression inside of a case expression , so you could have the same Case statement repeated with a different if expression .
val typeofDay = dayofWeek match{
case "Monday" | "Tuesday" => "Working Day"
case "Saturday" => "Sabbath"
}
in java you have default to do the catch everything else, Scala expands upon this option by giving us two options , you can either declare another case stmt with another variable that catches the default value that can be used in the expression or you can use the default wild card operator “_”
case someothervalue => s"This $somethervalue could be sunday or other working day"
or
case _ => s"This $dayoftheweek could be sunday or other day"
notice i cannot use the $_ to refer to the variable in the expression
a for loop s just like java , it just iterates through the list so its a statement , recollect statements are code that does not return anything, if you add a “yield” statement , then scala considers the for loop to be an expression which then returns a value for each of the iteration
for(item <- iteminlist){
item match {
case "Mango" =>println("Fruit")
case everythingelse => println("it could be a fruit orsomething else")
}
// the above stmt doesnot return anything it just prints - the stmt below // returns a list
val new list = for(item <- iteminlist) yield
{
item match {
case "Mango" => "Fruit"
case everythingelse => "it could be a fruit or something else"
}
this will return a list
List(Fruit, ....)
in for loop you can use to for the index and it includes the last number in the range or until and it does not include the last number in the range.
while and do while statements are not preferred in scala , its a statement not an expr , the incremental variable has to be a var so it can b incremented and it doesnt fit the functional programming paradigm.
functions are first class citizens which means its an object in itself so it can be passed to other methods , function , store it in a val , return a function etc …all these things you cannot do with a method. methods are declared inside a class . Here is how you would declare the two
class test {
def m1(x:Int) = x+3
val f1 = (x:Int) => x+3
}
methods have a “def” and then the name and then an = sign and optionally curly brackets for the expression block.
you can easily convert scala method to function …there are two ways one is by specifying the method signature and the other by wildcard “_” appended to the method ..this is called as eta and scala compiler understands and converts the method to function.
we can convert method m2 to function f2 as follows
def m2(x:Int):Int = { x+ 3}
m2: (x: Int)Int
scala> val f1 = (x:Int)=> {x + 3.0}:Double
f1: Int => Double = $$Lambda$840/0x0000000801068840@26bb92e2
scala> val f2:(Int) => Int = m2
f2: Int => Int = $$Lambda$842/0x000000080106a040@77a2688d
you see both function f1 and f2 are of the type Lambda , prior version of scala would make it of type function1 or functionn where n = 1..23
the other more common way is the eta expansion
scala> val f3 = m2 _
f3: Int => Int = $$Lambda$843/0x000000080106a840@9499643
note the right side is the name of the method -m2 , space character and then " _" wild card character
with named method parameters , the method can be invoked out of order , since we are referring to parameters by name , see below
scala> def m3(num:Int, dem:Int):Float = {num/dem}
m3: (num: Int, dem: Int)Float
scala> m3(2,3)
res15: Float = 0.0
scala> m3(3,2)
res16: Float = 1.0
scala> m3(dem=2,num=3)
res17: Float = 1.0
scala>
in this case i am passing out of order , but scala knows because of named parameter , this works only with methods not with function
you can define a default value for a parameter in the method , so you dont have to pass the parameter. Java does not support default parameter and you have to use function overloading in java to get that same feature
parametric polymorphism is same as generics in java with Type parameter. Type parameters are passed in square brackets [] unlike method parameters which are sent in round brackets ()
type parameters only work with methods and not functions ..so if you convert such methods to functions with the eta expansion , it loses the type and converts everything to Any object so we have essentially lost type safety. The key here is to
scala> def printpairTypes[K,V](k:K,v:V) = {
| val keytype = k.getClass
| val valtype = v.getClass
| println(s"$k, $v are of types $keytype $valtype")
| }
printpairTypes: [K, V](k: K, v: V)Unit
scala> printpairTypes(10,"ten")
10, ten are of types class java.lang.Integer class java.lang.String
val printfn = printpairTypes _
printfn: (Any, Any) => Unit = $$Lambda$1020/0x000000080111d840@70e75151
converting to function automatically converts to ( Any , Any ) and we have lost type safety, if instead we define the types explicitly , it picks it up
scala> val printfn1 = printpairTypes[Int, String] _
printfn1: (Int, String) => Unit = $$Lambda$1028/0x0000000801128840@426ba1d5
varargs or variable arguments can be passed to a method ( not supported in functions ) by specifying * next to the type ..see example below
scala> def concatStrings(strings:String*) = {
| var concatanetedstr = ""
| for (s <- strings) concatanetedstr = concatanetedstr + "" + s
| concatanetedstr
| }
concatStrings: (strings: String*)String
scala> concatStrings("Hello" , "World" , "From" , "Ella")
res23: String = HelloWorldFromElla
( i am trying a new wordpress plugin for syntax highlighter midway through the post , so the code section can be a bit more readable …whats life without constant change )
concatStrings("Hello" , "World" )
res24: String = HelloWorld
so the same method is invoked with different number of parameters or variable arguement or vararg
procedures are named reusable statements , functions are named reusable expressions and since statements dont return anything , procedure returns unit which is equivalent to void in java
we can define methods with or without parantheses …its allowed and makes it look like fields
nested functions are allowed , we can return a tuple of values from the outer function
Higher order function can take a function as an argument or return a function. you can pass parameters and the function that needs to be invoked as another parameter to another function in this case a higher order function . similarly if the last statement in a function is an anonymous function then that become the return value so you return a function. This helps with currying. i have described a lot in the last two statements. This needs a separate post by itself.
here is a sample higher order function
def math(x:Double,y:Double,fn:(Double,Double)=>Double):Double = {fn(x,y)}
math: (x: Double, y: Double, fn: (Double, Double) => Double)Double
scala> val result = math(10,20,(a,b)=>a + b)
result: Double = 30.0
// in the case above we are passing the function ( or rather the function definition ) // to the math function as is the case below
scala> val f2 = (a:Double,b:Double) => a + b
f2: (Double, Double) => Double = $$Lambda$907/0x00000008010a0840@6f4adaab
scala> val result = math(10,20,f2)
result: Double = 30.0
// in this case math is a higher order function since it accepts a function f2 as a parameter
partially applied function is way to fix or bound one or many parameters of an existing function ,this promotes code reuse . in Java we would use overloading to achieve the same functionality .
you can group similar parameters into parameter groups , so you can have functions that take in multiple parameter groups fn()() …this idea of writing a function with multiple parameter groups is called Currying
you can specify any of the parameter groups and you get partially applied functions ..
closure = nested function + all of the variables local to the outer scope ( Referencing environment )