Scala
unifying functional and object-oriented programming
A functional language has these characteristics:
• Functional programs are made up of functions.
• A function always returns a value.
• A function, given the same inputs, will return the same values.
• Functional programs avoid changing state or mutating data. Once you’ve set a value, you have to leave it alone.
Strictly speaking, Scala is not a pure functional programming language, just like C++ is not a pure object-oriented language.
Functional Programming and Concurrency
... Functional programming languages can solve these problems by eliminating mutable state from the equation. Scala does not force you to completely eliminate mutable state, but it does give you the tools to code things in a purely functional style.
(with some small exceptions) everything is an object in Scala: Integers and Strings are all objects. Scala is strongly typed (strong, static typing)
(Statically typed languages enforce polymorphism based on the structure of the types. Is it a duck by the genetic blueprint (static), or is it a duck because it quacks or walks like one? Statically typed languages benefit because compilers and tools know more about your code to trap errors, highlight code, and refactor. The cost is having to do more work and living with some restrictions.)
scala> val a = 1
a: Int = 1
scala> val b = 2
b: Int = 2
scala> if ( b < a) {
| println("true")
| } else {
| println("false")
| }
false
no need to specify a type; Scala can infer the type, and bind types at compile time. ("val a:Int = 1" is fine, too)
"val" is immutable; "var" is not
"Nil" is an empty list. Neither "Nil" or "0" is a Boolean. (In Ruby, 0 evaluated to true; in C, 0 was false)
Loops:
def whileLoop {
var i = 1
while(i <= 3) {
println(i)
i += 1
}
}
whileLoop
def forLoop {
println( "for loop using Java-style iteration" )
for(i <- 0 until args.length) {
println(args(i))
}
}
forLoop
def rubyStyleForLoop {
println( "for loop using Ruby-style iteration" )
args.foreach { arg =>
println(arg)
}
}
rubyStyleForLoop
Range:
Like Ruby, Scala supports first-class ranges
scala> val range = 0 until 10
range: Range = Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> range.start
res2: Int = 0
scala> range.end
res3: Int = 10
scala> range.step
res4: Int = 1
scala> (0 to 10) by 5
res6: Range = Range(0, 5, 10)
scala> (0 to 10) by 6
res7: Range = Range(0, 6)
scala> (0 until 10 by 5)
res0: Range = Range(0, 5)
scala> val range = (10 until 0) by -1
range: Range = Range(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
scala> val range = 'a' to 'e'
range: RandomAccessSeq.Projection[Char] = RandomAccessSeq.Projection(a, b, c, d, e)
Tuple:
scala> val person = ("Elvis", "Presley")
person: (java.lang.String, java.lang.String) = (Elvis,Presley)
scala> person._1
res9: java.lang.String = Elvis
scala> val (x, y) = (1, 2)
x: Int = 1
y: Int = 2
Class
scala> class Person(firstName: String, lastName: String)
defined class Person
scala> val gump = new Person("Forrest", "Gump")
gump: Person = Person@7c6d75b6
Constructor:
class Person(firstName: String) {
println("Outer constructor")
def this(firstName: String, lastName: String) {
this(firstName)
println("Inner constructor")
}
def talk() = println("Hi")
}
val bob = new Person("Bob")
val bobTate = new Person("Bob", "Tate")
batate$ scala code/scala/constructor.scala
Outer constructor
Outer constructor
Inner constructor
When there’s something that can have only one instance, you’ll define it with the object keyword instead of the class keyword.
object TrueRing {
def rule = println("To rule them all")
}
TrueRing.rule
The TrueRing definition works exactly like any class definition, but it creates a singleton object.
Inheritance:
class Person(val name: String) {
def talk(message: String) = println(name + " says " + message)
def id(): String = name
}
class Employee(override val name: String,
val number: Int) extends Person(name) {
override def talk(message: String) {
println(name + " with number " + number + " says " + message)
}
override def id():String = number.toString
}
val employee = new Employee("Yoda", 4)
employee.talk("Extend or extend not. There is no try.")
Scala uses a concept called companion objects to mix class and instance methods on the same class. Where Ruby uses mixins and Java uses interfaces, Scala uses a structure like a mixin called a Trait .
Trait:
class Person(val name:String)
trait Nice {
def greet() = println("Howdily doodily.")
}
class Character(override val name:String) extends Person(name) with Nice
val flanders = new Character("Ned")
flanders.greet
Above is about OO, below is about Functional Programming.
Function declarations:
scala> def double(x:Int):Int = x * 2
double: (Int)Int
scala> double(4)
res0: Int = 8
scala> def double(x:Int):Int = {
| x * 2
| }
double: (Int)Int
scala> double(6)
res3: Int = 12
Mutable state is bad. When you declare variables, you should make them immutable whenever you can to avoid conflicting state. In Java, that means using the final keyword. In Scala, immutable means using val instead of var:
scala> var mutable = "I am mutable"
mutable: java.lang.String = I am mutable
scala> mutable = "Touch me, change me..."
mutable: java.lang.String = Touch me, change me...
scala> val immutable = "I am not mutable"
immutable: java.lang.String = I am not mutable
scala> immutable = "Can't touch this"
<console>:5: error: reassignment to val
immutable = "Can't touch this"
^
It’s best to avoid var when you can for better concurrency. This basic design philosophy is the key element that differentiates functional programming from object-oriented programming: mutable state limits concurrency.
Scala’s primary collections are lists, sets, and maps.
scala> List(1, 2, 3)
res4: List[Int] = List(1, 2, 3)
scala> List("one", "two", 3)
res6: List[Any] = List(one, two, 3)
scala> List("one", "two", 3)(2)
res7: Any = 3
List access is a function, so you use () instead of [] . Scala’s index for list starts with 0, as it does with Java and Ruby.
Nil in Scala is an empty list:
scala> Nil
res33: Nil.type = List()
(omit Set, Map ...)
Foldleft:
scala> val list = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)
scala> val sum = (0 /: list) {(sum, i) => sum + i}
sum: Int = 6
/: is an operator with initialValue /: codeBlock
The syntax of the other version of foldLeft will seem strange to you. It uses a concept called currying. Functional languages use currying to transform a function with multiple parameters to several functions with their own parameter lists. Just understand that what’s going on under the covers is a composition of functions rather than a single function.
scala> val list = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)
scala> list.foldLeft(0)((sum, value) => sum + value)
res54: Int = 6
XML as a first-class programming construct:
scala> val movies =
| <movies>
| <movie genre="action">Pirates of the Caribbean</movie>
| <movie genre="fairytale">Edward Scissorhands</movie>
| </movies>
movies: scala.xml.Elem =
<movies>
<movie genre="action">Pirates of the Caribbean</movie>
<movie genre="fairytale">Edward Scissorhands</movie>
</movies>
scala> movies.text
res1: String =
Pirates of the Caribbean
Edward Scissorhands
scala> val movieNodes = movies \ "movie"
movieNodes: scala.xml.NodeSeq =
<movie genre="action">Pirates of the Caribbean</movie>
<movie genre="fairytale">Edward Scissorhands</movie>
scala> movieNodes(0)
res3: scala.xml.Node = <movie genre="action">Pirates of the Caribbean</movie>
scala> movieNodes(0) \ "@genre"
res4: scala.xml.NodeSeq = action
Pattern Matching and Guards:
def factorial(n: Int): Int = n match {
case 0 => 1
case x if x > 0 => factorial(n - 1) * n
}
println(factorial(3))
println(factorial(0))
Scala has first-class regular expressions. The .r method on a string can translate any string to a regular expression.
scala> val reg = """^(F|f)\w * """.r
reg: scala.util.matching.Regex = ^(F|f)\w *
scala> println(reg.findFirstIn("Fantastic"))
Some(Fantastic)
scala> println(reg.findFirstIn("not Fantastic"))
None
XML with matching:
val movies = <movies>
<movie>The Incredibles</movie>
<movie>WALL E</movie>
<short>Jack Jack Attack</short>
<short>Geri's Game</short>
</movies>
(movies \ "_").foreach { movie =>
movie match {
case <movie>{movieName}</movie> => println(movieName)
case <short>{shortName}</short> => println(shortName + " (short)")
}
}
Concurrency
The primary constructs are actors and message passing. Actors have pools of threads and queues. When you send a message to an actor (using the ! operator), you place an object on its queue. The actor reads the message and takes action. Often, the actor uses a pattern matcher to detect the message and perform the appropriate message.
import scala.actors._
import scala.actors.Actor._
case object Poke
case object Feed
class Kid() extends Actor {
def act() {
loop {
react {
case Poke => {
println("Ow...")
println("Quit it...")
}
case Feed => {
println("Gurgle...")
println("Burp...")
}
}
}
}
}
val bart = new Kid().start
val lisa = new Kid().start
println("Ready to poke and feed...")
bart ! Poke
lisa ! Poke
bart ! Feed
lisa ! Feed
to do things asynchronously:
import scala.io._
import scala.actors._
import Actor._
// START:loader
object PageLoader {
def getPageSize(url : String) = Source.fromURL(url).mkString.length
}
// END:loader
val urls = List("http://www.amazon.com/",
"http://www.twitter.com/",
"http://www.google.com/",
"http://www.cnn.com/" )
// START:time
def timeMethod(method: () => Unit) = {
val start = System.nanoTime
method()
val end = System.nanoTime
println("Method took " + (end - start)/1000000000.0 + " seconds.")
}
// END:time
// START:sequential
def getPageSizeSequentially() = {
for(url <- urls) {
println("Size for " + url + ": " + PageLoader.getPageSize(url))
}
}
// END:sequential
// START:concurrent
def getPageSizeConcurrently() = {
val caller = self
for(url <- urls) {
actor { caller ! (url, PageLoader.getPageSize(url)) }
}
for(i <- 1 to urls.size) {
receive {
case (url, size) =>
println("Size for " + url + ": " + size)
}
}
}
// END:concurrent
// START:script
println("Sequential run:")
timeMethod { getPageSizeSequentially }
println("Concurrent run")
timeMethod { getPageSizeConcurrently }
// END:script
The "receive" method is where the real work happens.
Core strengths:
1. Concurrency
2. Evolution of legacy Java
3. Domain-specific languages
4. XML
5. Bridging (functional programming and OO)
Weaknesses:
1. Static typing
2. Syntax
3. Mutability