An introduction to Scala
An Introduction to Scala
Overview
- Introduction
- Why is it gaining popularity, and why should you care?
- History
- Functional programming
- Syntax
- Data structures
- Benefits
- Drawbacks
- Conclusion
Introduction
- Scala = scalable language.
- From scripts all the way up to enterprise
- Runs on JVM
- Combines functional and object-oriented paradigms
Scala was designed to be both object-oriented and functional. It is a pure object-oriented language in the sense that every value is an object. Objects are defined by classes, which can be composed using mixin composition. Scala is also a functional language in the sense that every function is a value. Functions can be nested, and they can operate on data using pattern matching. Language creator Martin Odersky
Why?
- Productive like a scripting language, but with type safety and speed
- You can express yourself in fewer lines of code than equivalent Java.
- “On average, Odersky predicts a 2x reduction in lines of code between a Java program and a Scala port.” Scala lift-off: Martin Odersky Keynote
- Less boilerplate
- Better type inferencing
- Well suited to multithreaded environments
- Actors and message passing
Less boilerplate
// Scala
case class Person(firstName: String, lastName: String)
// Java
public class Person implements Serializable {
private final String firstName;
private final String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public Person withFirstName(String firstName) {
return new Person(firstName, lastName);
}
public Person withLastName(String lastName) {
return new Person(firstName, lastName);
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Person person = (Person) o;
if (firstName != null ? !firstName.equals(person.firstName) : person.firstName != null) {
return false;
}
if (lastName != null ? !lastName.equals(person.lastName) : person.lastName != null) {
return false;
}
return true;
}
public int hashCode() {
int result = firstName != null ? firstName.hashCode() : 0;
result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
return result;
}
public String toString() {
return "Person(" + firstName + "," + lastName + ")";
}
}
Samples where Scala code is significantly shorter than Java
History
- Martin Odersky created it
- Functional programming and compiler background (wrote current javac reference compiler)
- Worked on adding generics to Java
- Design began in 2001, released in 2003
- Version 2.80 Final released July 14, 2008
Functional programming
- Avoid state and mutable data
- Every variable is a constant
- Lends itself to parallelization; avoid locking and deadlocks
- First-class functions – functions can serve as arguments and results of functions
- Recursion is primary tool for iteration
- Heavy use of pattern matching
- Lazy evaluation – infinite sequences
Who’s using Scala
- Twitter uses for performance critical code; replaces Ruby in that regard
- FourSquare uses Scala and Lift web framework
- Xerox uses Scala and Lift
- Etherpad uses Scala in its real time document collaboration software (acquired by Google for Google Docs; Scala code is now opensourced)
Syntax:
Hello world in Scala:
object HelloWorld extends Application {
Console.println("Hello World")
}
No semicolons needed
Variables declared with val and var
val x = 0 // immutable
var y = 0 // mutable
x = 1 // compiler error
y = 1 // fine
Type declarations occur after the variable
int x = 0; // Java
val x: Int = 0 // Scala
Generic type arguments occur within square brackets rather than angle brackets
List<Integer> intList = ...; // java
val intList: List[Int] = ..// scala
Array indexing is done through use of parentheses rather than square brackets
// Java
int[] x = {1,2,3,4};
x[0] // 1
// Scala
val x: Array[Int] = Array(1,2,3,4)
x(0) // 1
Method definitions
scala> def square(x:Int) = { x * x }
square: (Int)Int
scala> square(5)
res38: Int = 25
No such thing as a ‘void’ method, every method returns a value. void = “Unit” in Scala
scala> def printIt(x:Any) = { println(x) }
printIt: (Any)Unit
Type inferencing – note that I haven’t been declaring what types my methods take
// Compiler infers that the return type must be int
scala> def square(x:Int) = { x * x }
square: (Int)Int
// I can explicitly indicate what it returns if I prefer
scala> def squareExplicit(x:Int):Int = { x * x }
squareExplicit: (Int)Int
No need to explicitly return values
scala> def square(x:Int) = { x * x }
square: (Int)Int
scala> def squareExplicit(x:Int):Int = { return x * x }
squareExplicit: (Int)Int
// The result of last expression is returned.
Conditionals as values
// BAD. Java style, mutable value
var x = 0
if (someCondition) {
x = something
}
else {
x = somethingElse
}
// GOOD: Scala style, immutable
val x =
if (someCondition) {
something
}
else {
somethingElse
}
// or if short enough
val x = if(something) something else (somethingElse)
All operations are method calls
Scala doesn’t technically have operator overloading, because it doesn’t actually have operators in the traditional sense. Instead, characters such as +, -, *, and / can be used in method names. Thus, when you typed 1 + 2 into the Scala interpreter in Step 1 you were actually invoking a method named + on the Int object 1, passing in 2 as a parameter. As illustrated in Figure 3.1, you could alternatively have written 1 + 2 using traditional method invocation syntax (1).+(2).
“Programming in Scala, p. 39”
What good does that do me?
If your domain objects make sense to add, multiply, divide, etc., you can treat them in a much more natural way.
// BigInt is a wrapper around Java's BigInteger, providing arithmetic methods
scala> BigInt("103058235287305927350") + BigInt("288888203959230529352")
res16: BigInt = 391946439246536456702
// In Java you're stuck calling .add, .multiply, etc
scala> new java.math.BigInteger("103058235287305927350").add(new java.math.BigInteger("288888203959230529352"))
res21: java.math.BigInteger = 391946439246536456702
Data structures
Lists
More powerful than Java lists. Expose a whole raft of Functional Programming paradigms, such as map, reduce, filter, folds, etc.
// Note the syntax; you do not call "new". This is because there is
// an apply ("()") method defined in the List object.
scala> val a = List("hello","how","are","you")
a: List[java.lang.String] = List(hello, how, are, you)
// Explicit
val nums:List[Int] = List(1,2,3,4)
scala> a.size
res40: Int = 4
scala> a.mkString(" ")
res41: String = hello how are you
scala> a.map(x => x.toUpperCase())
res42: List[java.lang.String] = List(HELLO, HOW, ARE, YOU)
scala> a.map(x => x.length())
res43: List[Int] = List(5, 3, 3, 3)
scala> a.filter(x => x.startsWith("h"))
res44: List[java.lang.String] = List(hello, how)
scala> a.forall(x => x.length() > 0)
res46: Boolean = true
// Singly linked list
scala> a.head
res67: java.lang.String = hello
scala> a.tail
res68: List[java.lang.String] = List(how, are, you)
// add to head
scala> 1 :: List(2)
res70: List[Int] = List(1, 2)
// reverse
scala> a.reverse
res72: List[java.lang.String] = List(you, are, how, hello)
Tuples
Tuples are a good data structure to pack up multiple variables to return from a function, without the need to define a helper class to do so. Slightly syntax to create them, as well as to access the elements contained therein.
// Explicitly create the tuple; never really a need to do this
scala> val a:Tuple2[Int,Int] = (5,6)
a: (Int, Int) = (5,6)
// The parentheses indicate it's a tuple
scala> val b = (5,6)
b: (Int, Int) = (5,6)
// Yes, I know it's a strange syntax. And it's 1-based rather than 0-based. This is because Haskell has 1-based tuple ordering and Odersky wanted to stay true to that.
scala> a._1
res48: Int = 5
scala> a._2
res49: Int = 6
// You can have an arbitrary number of elements, of any type you want
val listTuple = (List(1,2,3), "hello", new java.awt.Color(255,0,0))
scala> val listTuple = (List(1,2,3), "hello", new java.awt.Color(255,0,0))
listTuple: (List[Int], java.lang.String, java.awt.Color) = (List(1, 2, 3),hello,java.awt.Color[r=255,g=0,b=0])
Maps
Scala provides immutable maps as well as a syntax for creating map literals.
scala> val theMap = Map("hello"->5,"world"->5)
theMap: scala.collection.immutable.Map[java.lang.String,Int] = Map(hello -> 5, world -> 5)
scala> theMap("hello")
res50: Int = 5
scala> theMap.getOrElse("world",2)
res51: Int = 5
scala> theMap.getOrElse("worldly",2)
res52: Int = 2
scala> theMap.keySet
res54: scala.collection.Set[java.lang.String] = Set(hello, world)
scala> theMap.values
res55: Iterator[Int] = non-empty iterator
scala> theMap.values.toList
res56: List[Int] = List(5, 5)
// Mutable map:
var mutableMap = Map[Int, String]()
mutableMap += (1 -> "1")
mutableMap += (2 -> "2")
Arrays
Internally map to Java arrays. Idiomatic Scala code tends to prefer Lists to Arrays, but they are necessary for interacting with a lot of Java code. Create in same way as lists.
scala> val array = Array(1,2,3)
array: Array[Int] = Array(1, 2, 3)
Benefits
-
Static type checking but with improved type inference
// Java
private Map wordCount = new HashMap();
// Scala:
var wordCount:Map[String, Int] = Map() -
Much faster than Python/Ruby and other interpreted languages, but just as expressive
- Duck typing support
- Conciseness
- Functions as first class objects; no need for anonymous classes and extraneous interfaces Unified types
- Traits are a cross between interfaces and abstract classes, allowing you to provide default implementations. Allows for ‘mix-in’ composition, as well. Solves problems with multiple inheritance, while giving flexibility. Avoid repeating yourself.
- Console REPL environment allows you to quickly iterate and test
- Of theoretical interest
- Combines functional and object-oriented in statically-typed environment
Drawbacks/Complaints
- Immaturity of tool chain
- Binary/source incompatibility from rapid change of language
- Type system is very complex
- Functional programming can be hard to wrap head around for people familiar with OOP
-
Syntax can be so concise as to be cryptic
For instance, the following example comes from StackOverflow post.
def scanLeft[a,b](xs:Iterable[a])(s:b)(f : (b,a) => b) = xs.foldLeft(List(s))( (acc,x) => f(acc(0), x) :: acc).reverse
And then use it like this:
scala> scanLeft(List(1,2,3))(0)(_+_) res1: List[Int] = List(0, 1, 3, 6)
Conclusion
Scala is a very intriguing language due to its combination of object orientation with functional programming concepts. The programmer is not forced to choose between the expressiveness of objects and immutability; he can switch between the two paradigms as the situation warrants.
While binary/source incompatibility is a very real problem for enterprise adoption, it is an issue that the creators are working on.
Scala provides a great framework in which to explore functional programming methods, especially through the use of the interactive console environment.
Resources
- Scala in the Interprise
- Martin Odersky’s Introduction to Scala video
- Scala for Java refugees
- Top five scripting languages on the JVM
- Scala history
- Scala language homepage
- Scala cheatsheet
- Scala FAQ
- Java, Grovvy & Scala: side to side1 2
- Lift
- Scala is unfit for serious development
- What is stopping you from switching to Scala?