Archive

Posts Tagged ‘boilerplate’

An introduction to Scala

July 15, 2010 1 comment

Functional programming plus Object Oriented Programming = 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.
  • 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

Images: