Handy Tips in Scala
In this post, I would like to share few practical details I learnt from working on Scala. Sure that many thousands can be added under this. Still, would like to contribute a few as part of this blog.
1)Scala has built-in type inference mechanism. So programmer can define variables/literals without type information based on the scenario.Scala compiler can deduce the type of a variable from the expression(not always). But you should always provide type annotation to the parameters of a function whereas the return values can be inferred by Scala compiler.
Eg,
def function1(a:Int,b:String)={
return b.toInt+a
}function1(1,"5")
Result is, val res0: Int = 6
As you can see the return type is automatically inferred but if you omit specifying the datatype for the parameters of the function, you will get compilation error.
Note: Function with explicit return keyword specified should have datatype specified for the returned value.
If the same function is rewritten-ed with return keyword, then you must specify the return value datatype else compilation error will be thrown.
2)Functions are first class constructs. We have covered this under part 1 post.
3)Literals/Vals, not explicitly specified.
Eg,
for (arg <- args)
println(arg) // arg here is val.You cannot change value of arg. (or)def function1(a:Int)={
a=7 // Will result in error.All the parameters passed to a function are implicitly val. You cannot change values
}
4)Operators are methods. And any method can be an operator.
Operators are methods:
1+2 => here ‘+’ is an operation. Actually what is happening here is, 1 is of class Int and 2 is of class Int. Class Int has a method ‘+’ that takes another Int and returns an Int.
To verify this you can try implementing the same as (1).+(2) and the result is val res0: Int = 3.Similarly, all the operators we use such as -,%,/,*,<,>!,>=,<= etc can also be rewrittened as specified above.
Any method can be an operator:
“abc”.equals(“adf”) => Here equals is a methods implemented on String class. But you can rewritten the same in operator notation,
“abc” equals “adf”
Similarly other methods belonging to other classes can also be rewritten-ed in the same way.
5) When you have a variable named, you cannot use the same name for other variables in the same scope. Scala allows you to have same name for two variables but with different scope.
val a=1
val a=2 // Doesnot compile. It causes error.The following code snipped works because of the two different scopes.
val a=1
{
val a=2
println(a) // prints 2
}
println(a) // prints 1In Interpreter, when you give,
val a=1
val a=2 // The compiler automatically creates both the literals in different scopes and so it doesnt throw any error.
6) && and || operations use shortcuts for execution as in java. These operators don’t evaluate the right hand side, provided the left hand side of the operator sufficient enough to yield the result.
def function1()={
println("left side")
false
}def function2()={
println("right side")
true
}function1 && function2Output:
left side
val res20: Boolean = falseAs you can clearly see, right hand side function function2 isn't executed to evaluate the result.function1 & function2Output:
left side
right side
val res21: Boolean = falseWhereas above, you can see both the sides get executed, because of the usage of & operator.
7) :+ and =+ are different. But they both are used to add elements.
:+
val a=List("hellow","hi")
a:+"hey"
What’s happening here is “hey” is appended to the existing list and in-turn it generates a new list with “hey” added. Even when you change list a to var, the same happens because List is immutable. The below set of operations doesn’t change the value of the existing list but creates a new list with “hey” added to it.
var a=List("hellow","hi")
a:+"hey"
We can try performing the same operation on Array which is mutable.
Note: Array is mutable ie, in-place change of value is allowed in Array but the size(or length) is fixed.
val a_array=Array("hellow","hi")
a_array(0)="changed"
Now a_array is Array(changed, hi). Despite being val, you are able to do in-place value change in a_array, because a_array is Array ie,mutable.
Having a_array as var has nothing to do with extending its size. It means performing :+ on mutable variable a_array gives the same result, ie, creating a new array with “hey” added to it.
var a_array=Array("hellow","hi")
a_array:+"hey" //Array(hellow, hi, hey) -> new aray created
For now, we have seen List is immutable, Array is mutable (allows in-place changes). And :+ appends new element and creates a new Array/List.
+=
val a=List("hellow","hi")
val a_array=Array("hellow,"hi")
a+="hey" // generates compilation error that += is not member of List[String]
a_array+="hey" " // generates compilation error that += is not member of Array[String]
The compilation error is because += tries to append the element to the existing list/array. As we discussed above, List/Array cannot have their size changed. So += are not allowed on List/Array.
We can use += operator on ListBuffer/ArrayBuffer or other classes which allows change in size.
import scala.collection.mutable.ListBufferval a_listbuffer=ListBuffer("hellow","hi")
a_listbuffer+=("hey") // you can see "hey" is added to a_listbuffer itself.
8)Explanation for certain rich methods in Scala:
foldLeft:
foldLeft executes the function(parameter passed) from the left side, with the accumulator being accumulated on the left hand side.
Its better to see the execution through an example for better understanding,
val list_a=List("first","second","third")
list_a.foldLeft("appending")(_+_)
‘appending’ is the initial accumulator value and the function passed in as a parameter (_+_) to foldLeft, keeps on concatenating the elements of the list. This is the core of the code given above. Now lets breakdown the execution and see how the result is formed. Execution breakdown helps us to make use of foldLeft method in various use-cases.
list_a.foldLeft("appending")(_+_)Leftside + Rightside("appending" + "first")
("appendingfirst" + "second")
("appendingfirstsecond" + "third")
// Output is "appendingfirstsecondthird"
foldRight:
foldRight executes in the same way as foldLeft except that it starts reading the elements from the right side with accumulator value getting accumulated with right hand side.
list_a.foldRight("appending")(_+_)Leftside + Rightside("third"+"appending" )
("second" + "thirdappending")
("first" + "secondthirdappending")
// Output is "firstsecondthirdappending"
dropWhile:
When apply dropWhile on list or array, there might be some misconception that all the elements matching the condition given may be dropped. But the truth is, dropWhile drops only the first element it encounters matching the condition given. dropWhile works similar to while loop, dropping the first element meeting the criteria.
val a_list=List("hellow","hey","hi","hellow","greetings")
a_list.dropWhile(i=>i=="hellow")
Output,
List(hey, hi, hellow, keybl)
As you can see, the first element equivalent to ‘hellow’ is only dropped.
9) Always preferable to use get method on Map instances than directly accessing using the key value.
Consider you have a Map literal,
val a_map=Map(1->"hellow",2->"hey",3->"hi")
a_map.get(1) // Output is "hellow"
a_map(1) // Output is "hellow"
The above is safe play since we have 1->”hellow” keyvalue pair in the Map a_map. But when you try accessing the pair which has no key defined on it, the compiler will throw exception in direct access whereas returns None, while using get method.
a_map.get(0) // Output is Option[String] = None
a_map(0) // Scala compiler throws java.util.NoSuchElementException exception.
10)Singleton objects:
We all know singleton object is a class that has exactly one instance. When you assign two literals of same singleton object at the same time, the latest assignment takes the scope.
Consider you have the following singleton object,object singleton_example{
var first_variable:String = _
var second_variable:String = _
}
val obj1= singleton_example
obj1.first_variable="hey there!!"
obj1.second_variable="hellow there!!"
println(obj1.first_variable) // Output is "hey there!!"val obj2=singleton_example
obj2.first_variable="welcome home!"
obj2.second_variable="have a good day!!"println(obj1.first_variable) // Output is "welcome home!"
println(obj2.first_variable) // Output is "welcome home!"
As you can see, the latest object literal replaces the previous one, since only one instance can exist at a time for singleton objects.
11)Function literals:
We have seen what is function literal in our last post. I just want to show an example where we can also pass parameters to the literals.
val increase = (x: Int) => x + 1
increase(1) // Output is 2
increase(8) // Output is 9
12)In Scala, fields and methods belong to the same namespace. It allows a parameter-less method to be overridden by a field in Scala. On the other hand, it forbids having the same name to both a method and field in the same class, whereas its allowed in Java.
The following piece of code compiles successfully in Java,
class CompileCheck {
int f = 0;
int f() {
return 1;
}
The above code compiles fine in Java, because it has four namespace such as fields, methods, types and packages. And Scala has two namespaces, • values (fields, methods, packages, and singleton objects) and • types (class and trait names). And so the above piece of code in Scala will cause compilation error.
class CompileCheck{
val function1:Int=0
def function1():Int= { return 1 }
}Compilation error:
method function1 is defined twice;
the conflicting value function1 was defined