Scala Tutorials Part #12 - Infix notation
Originally Posted On : 28 Feb 2017
Infix Notation
We already saw what infix notation was in part 7 in a very brief manner.
In this article we are going to see what it is in detail and where it actually helps.
This is part 12 of the scala tutorial series. Check here for the full series.
Index
- Dot notation in java
- Infix notation introduction
- Dealing with verbosity
- Avoiding infix in suffix notation methods
- N-Arity methods
Dot notation in java
The regular way to call methods in a language such as java
would be like,
public class Test {
public static void main(String[] args) {
Test t = new Test();
t.printSomething();
}
public void printSomething() {
System.out.println("Hello from a java method");
}
}
The dot indicates that it is a method call. It is also used to refer variables as well.
public class Test {
int a = 10;
public static void main(String[] args) {
Test t = new Test();
t.printSomething();
System.out.println(t.a);
}
public void printSomething() {
System.out.println("Hello from a java method");
}
}
Infix notation introduction
We already saw that even the +
operation is in fact a method call in part 7. It is a pretty good example of how this works.
The .+
can be called without the .
and with the +
alone and even with spaces. This is a feature of the scala compiler and not syntactic
sugar i.e its built right into the language.
Let’s understand with our own example.
Consider a case class which can represent a complex number and have an addition operation inside it.
case class ComplexNumber(real: Double, imaginary: Double) {
def +(number: ComplexNumber): ComplexNumber =
ComplexNumber(real + number.real, imaginary + number.imaginary)
}
This defines a method with the name +
and adds two instances of the ComplexNumber
class. Not to be confused with recursion, here we are just calling
the constructor with different values.
As we saw in part 6 creating instances of these is pretty simple. We can even create two instances and make use of the plus method to add them together.
val a = ComplexNumber(2, 5)
val b = ComplexNumber(1, -3)
val c = a.+(b)
Using infix notation we can call the method +
without using the dot.
val a = ComplexNumber(2, 5)
val b = ComplexNumber(1, -3)
val c = a.+(b)
val d = a + b
Dealing with verbosity
If we don’t have the infix notation option then we would have deal with the below syntax.
val dotNotation = ComplexNumber(10,7).+(ComplexNumber(12,44)).+(ComplexNumber(55,4))
Life is much more simpler with infix notation.
val dotNotation = ComplexNumber(10,7).+(ComplexNumber(12,44)).+ (ComplexNumber(55,4))
val infixNotation = ComplexNumber(10,7) + ComplexNumber(12,44) + ComplexNumber(55,4)
Not only that the syntax is natural, but there are lesser chances to make mistakes.
Verbosity does not necessarily mean that it is bad, but it should not get in the way of a programmer, there is a fine line that divides where it helps and where it is just a headache.
The Bigint type which we saw earlier is an another good example of this can be useful.
A programmer need not invent special syntax to work with complex types. In fact with this syntax one can write almost any type that seems sensible
and give all this syntactic sugar over it such +
,-
etc.,
Avoiding infix in suffix notation methods
Now that we understand how this notation thingie works, there are cases where they should not be used.
In part 3 we saw that a method which has does not take any parameters and returns nothing is called as a 0 arity method and it can be called either with or without an empty circular param depending on how the original method is defined. This is called suffix notation.
One important point to note is that this notation should be used only if it is a pure function.
This is a situation where the infix notation would cause a problem. Let’s take the below example.
As explained in the docs, since there is no semicolon after the a x
infix call, it considers the next statement i.e the println
also as a method argument.
If we gave the method a dummy parameter of the Unit
type as below.
val a = new A
a x
println("b")
class A {
def x(f: Unit): Unit = println("a")
}
Then something unexpected happens. It prints out b
and then a
as opposed to a
and then b
. This occurs since the entire println
is passed as an argument to the x
method.
I also encourage to debug through the code for more clarity.
So it is better to follow the regular dot notation and avoid infix for the above suffix/no argument pure functions.
val a = new A
a.x
println("b")
class A {
def x(): Unit = println("a")
}
Works as expected i.e printing a
before b
.
N-Arity methods
Any method that has greater than zero arguments can be considered as an n-arity method where n is the number of arguments to that method.
As always, an example speaks more than words.
val list = List(1,2,3,4)
println(list mkString "|")
mkString
is a method which takes one string argument as a separator and then turns the given list to a String
.
If there is more than one argument, then it needs to be wrapped up to into a param.
val string = "Hi there"
//Wrong - Will result in compilation error
string substring 1,3
//Correct
string substring (1,3)
Infix notation is one of the core concepts in scala and it is used all over the place. It is particularly useful to design Domain Specific Languages(DSLs). A good understanding of it is necessary.
Stay tuned