Scala is an object oriented and functional language that compiles to class files for use on the JVM (same as Java). Biggest advantage is that it provides very powerful functional programming concepts as well as a number of useful programming tools for writing cleaner or less code (two major complaints about Java is that it is overly verbose and slow to evolve).
This file explains the basics to Scala as well as some cool features about it. The point is to make it clear that it's probably not going to be a difficult jump to use and in fact may be completely worthwhile.
Most of Scala's basic types are very similar to Java's. Main difference is that they're all objects. And thus, we write the type in uppercase (Scala code conventions are very similar to Java's). Also, types now go after the variable name. An example of a simple function is shown below:
def greet(name: String) = {
println("Hello " + name)
}
From this code, we can see some other things about Scala. First of all, printing is done with the much less verbos println
, which works the same as in Java. We don't need to specify the return type of the function. This isn't because the function returns nothing (in fact, things that would return nothing -- aka void
-- in Java return Unit
in Scala), but rather because Scala is intelligent enough to know what type you are using. We still have to specify the types in arguments, of course.
Here's another example of this type inference:
scala> val someNumber = 5
someNumber: Int = 5
scala> val complexType = Seq(1, 2, 3)
complexType: Seq[Int] = List(1, 2, 3)
These are typed into the Scala REPL (read-evaluate-print loop -- it's very much like an interpretter), by the way.
One thing we can note from her is the val
keyword, which declares a value. A value is an immutable variable (one that cannot be changed). Scala loves immutability and it's preferred to use immutable types when practical (which is the vast majority of the time, in my experience). There's also the var
keyword for variables that can be changed. Note that this only applies to the variable. Some data types are immutable by encapsulation (Scala's String
is actually Java's String
).
The Seq
type is the general purpose sequence of items. There's also Array
, which is slightly different from how Java handles arrays (in particular, elements are accessed by parenthesis, eg, myArray(2)
). ArrayBuffer
is the mutable version.
scala> val array = ArrayBuffer(1, 2, 3)
array: scala.collection.mutable.ArrayBuffer[Int] = scala.collection.mutable.ArrayBuffer(1, 2, 3)
scala> array += 4
res0: array.type = ArrayBuffer(1, 2, 3, 4)
Scala extends the existing Java types with new methods. For example, Int
has a method to
that generates a sequence to another number:
scala> 1.to(5)
res2: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)
This method is not part of Java's Integer
class, but rather comes from RichInt
, which can be thought of as a bunch of methods added to Int
, kind of like C#'s extension methods. It's actually an automatic conversion (called an "implicit conversion") from Int
to RichInt
. Speaking of which, Scala uses implicit data in many ways. For example, a function can take in an implicit argument, which means that there simply has to be a variable declared implicit
within the function's scope and it will be automatically passed to the function. One use of this is when you have a number of database functions that need the current database session passed to them -- now the session can be passed automatically instead of having to manually do it every function call.
Also of note is that Scala lets us skip the dot to access members and we don't need the parenthesis for single arguments. This is really only done for simple cases. So the above code could have been written as:
scala> 1 to 5
res3: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)
Anyway, Option
is a very important type in Scala. Scala heavily frowns on the use of null
. Instead, we use Option
to show that a value might not exist. There are exactly two possible types of Option
s: Some(foo)
, where foo
is the data contained in the Option
and None
, which can be thought of like null
. The reason we use Option
is because it makes it clear that you must consider the possibility that there is no value.
Try
is very similar. It will be either Success(foo)
(where foo
is the data contained) or Failure(ex)
(where ex
is an exception). This is what we prefer to use instead of try-catch blocks. So we can actually use a variable to indicate success. An example is a function that has to get data from a database. If it succeeds, it returns a Success
with the fetched data. If it fails, it returns a Failure
containing the exception. Usage is simple:
scala> scala.util.Try {
println("Doing some 'risky' code")
5 // Not actually risky
}
Doing some 'risky' code
res7: scala.util.Try[Int] = Success(5)
As an aside, the above code demonstrates an important thing about Scala: we don't usually use the return
keyword, but instead the last value in a block of code is the returned value from that block of code (same thing that Haskell does). Also, we don't have to use parenthesis to surround arguments, but can instead use curly braces (which are necessary for cases such as this one, where we have multiple lines. So those lines of code are merely an argument to a function in the Try
functor (they're a special type of argument called "pass by name", which just passes some code that won't be evaluated until the function wants to).
And an example of some code failing:
scala> scala.util.Try {
"foo".toInt
}
res9: scala.util.Try[Int] = Failure(java.lang.NumberFormatException: For input string: "foo")
As mentioned, Seq
is the general type for ordered sequences. Vector
is the most common implementation. Scala's Vector
is like Java's ArrayList
(and thus Seq
is like Java's List
). Thus, Seq
is the preferred type for listing things.
Map
also exists in Scala and is quite similar to how Java's maps work. But better, like most of Scala. We can represent maps with code like:
scala> Map("key" -> 1, "another" -> 2)
res1: scala.collection.immutable.Map[String,Int] = Map(key -> 1, another -> 2)
Note that this is an immutable type (there's also a mutable version). This is very much like how String
is immutable in Java. So if we wanted to add to a map, we wouldn't modify it, but rather create a copy with the modification:
scala> Map("key" -> 1, "another" -> 2) ++ Map("a third" -> 3)
res2: scala.collection.immutable.Map[String,Int] = Map(key -> 1, another -> 2, a third -> 3)
The ++
operator appends maps (and many other types). It's not a built in operator, but rather just a function. This is because Scala supports function overloading and simply treats operators as functions (same as C++ does for user types).
Finally, Scala also has tuples. This provides a quick and easy way to pair meaningless data together. Tuples are easily used with parenthesis:
scala> (1, "hello")
res7: (Int, String) = (1,hello)
If statements return a value in Scala:
scala> val x = if("hello".size > 4) true else false
x: Boolean = true
You can also still use if statements the same as in Java. While loops are unchanged. For loops, however, are quite different. In fact, they work nothing like they do in Java. They're very similar to Python.
scala> for(x <- 1 to 5) print(x + " ")
1 2 3 4 5
There's also for comprehensions. These are basically a list of things to do in sequential order. They're like Haskell's do
notation:
scala> for {
somethingSafe <- Try(2 + 2)
somethingUnsafe <- Try(throw new Exception)
} yield somethingSafe
res5: scala.util.Try[Int] = Failure(java.lang.Exception)
scala> for {
somethingSafe <- Try(2 + 2)
somethingAlsoSafe <- Try(3 + 3)
} yield somethingSafe + somethingAlsoSafe
res6: scala.util.Try[Int] = Success(10)
It's really just running those commands in order, stopping when the first error is found. It obviously works better with functions that return Try
s. If there's no errors when we reach the end, the yield is used to determine the final value of the loop (they have values just like if statements).
Scala doesn't have the static keyword. Instead, you can specify an object
, which is a singleton object that's always in scope. So methods that belong to an object can be used exactly like a static Java method:
scala> object Foo {
val helloWorld: String = "Hello, World"
}
defined object Foo
scala> Foo.helloWorld
res0: String = Hello, World
By the way, we can note from here that parenthesis are unnecessary for function that need no arguments.
Scala also has case classes. This definition is going to seem lame, but case classes are glorious for cutting down on boiler plate code. A case class simply allows us to create a class with an automatically generated apply method (this looks like a constructor -- if the class is named Foo
then the apply method is what is called when we call Foo()
; constructors still use the new
keyword, but most Scala types have an apply method for constructing an object). Case classes also automatically create getters for their variables (they're immutable, so no setters).
Here's an example:
scala> case class Person(name: String, age: Int)
defined class Person
scala> val me = Person("Mike", 20)
me: Person = Person(Mike,20)
scala> me.name
res2: String = Mike
Case classes are also easier to use in pattern matching. Pattern matching is like a pimped up switch statement. Its usefulness is best illustrated via examples:
scala> me match {
case Person(name, age) =>
println("Hello, " + name)
println("You are " + age + " years old")
}
Hello, Mike
You are 20 years old
As we can see, it's able to match the fields of the case class, letting us extract the fields. This is more useful when we have a class like Try
, which can be either Success
or Failure
(they're both case classes).
scala> unsafeCode match {
case Success(value) =>
println("Value is " + value)
case Failure(ex) =>
println("Failed!")
}
Classes can also automatically generate getters and setters. Instead of a constructor being a method, we specify the arguments in a list after the class is declared and the body of the class is the constructor. If we specify arguments with the val
or var
keyword, they are given a getter or a getter + setter, respectively:
scala> class Bar(arg1: Int, val arg2: Double, var arg3: String) {
| println(arg1 + " " + arg2 + " " + arg3)
| }
defined class Bar
scala> val bar = new Bar(1, 2.0, "Hi")
1 2.0 Hi
bar: Bar = Bar@2bd609f
scala> bar.arg2 // arg2 has getter only
res8: Double = 2.0
scala> bar.arg3 = "Goodbye" // arg3 has getter + setter
bar.arg3: String = Goodbye
Mostly works the same way as Java. Traits are used instead of interfaces. They're much more powerful as they can add code to a class like how Python's mixins work. With that said, it should be possible to do everything we need to without directly using traits, so I won't go into more depth on them here.
What we've all been waiting for. We've got lambdas, which are a compact way of creating anonymous functions. This makes it easy to pass functions in. Here's a demo:
scala> Seq(1, 2, 3).map(x => x * x)
res10: Seq[Int] = List(1, 4, 9)
The map
method applies a function to each element in the sequence. The x => x * x
part is our lambda. The left of the =>
operator is the argument list (it's surrounded by parenthesis if there's more than one argument). The right side of that operator is the function body (which we can surround with curly braces if there's multiple lines). This simple code squares each item in the list (note that the result is a new Seq
).
filter
is a similar function that reduces a sequence to the items that match some predicate (condition). As an aside, we call functions like map
and filter
"higher level functions" -- they take in a function as an argument.
scala> (1 to 10).filter(x => x % 2 == 0) // Finding evens
res13: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10)
This is the preferred way to process items in a list. We rarely iterate through lists with regular loops. For those who have used C#'s LINQ before, this should feel familiar.
One thing we note about the above code is that it's somewhat verbose to have lambdas with a single argument. The argument list ends up taking up half of the code. Thus, Scala has a shorthand available, which is the underscore. In a lambda, the underscore can be used to symbolize the arguments (each use of the underscore will match the next argument in the arguments list). This reduces the code we have to write in some situations. So this:
scala> Seq(1, 2, 3).map(x => x + 1)
res14: Seq[Int] = List(2, 3, 4)
Can be shortened to:
scala> Seq(1, 2, 3).map(_ + 1)
res15: Seq[Int] = List(2, 3, 4)
Scala has raw strings, in which quotes and escapes will be ignored. They are used with triple double quotes:
scala> """And that's how I "saved" the day!"""
res16: String = And that's how I "saved" the day!
Scala also can create regex objects from strings (and raw strings are great because we don't have to escape all our backslashes). Regex can be used with pattern matching. As you likely already know, we can extract from regex by enclosing what we want to extract with parenthesis, which become a numbered group. Our pattern matching can match these groups:
scala> val iso8601Regex = """(\d\d\d\d)-(\d\d)-(\d\d)""".r
iso8601Regex: scala.util.matching.Regex = (\d\d\d\d)-(\d\d)-(\d\d)
scala> "2015-01-18" match {
case iso8601Regex(year, month, day) =>
println("Oy, vey, it's " + year + " already?")
}
Oy, vey, it's 2015 already?
Scala supports lazy evaluation, where some variable's value is not calculated until you use it. This is done by simply adding lazy
to the variable declaration. Here's an example of a variable that has an infinite loop in it, yet our program doesn't freeze because it hasn't evaluated the value (and thus hasn't been caught in the infinite loop), yet.
scala> lazy val x = while(true) { Unit }
x: Unit = <lazy>
Scala's generics are more powerful than Java's. Java lets you always downgrade any generic to store Object
s. Scala doesn't allow this, providing better type safety. In usage, generics mostly work the same, but use square braces instead of angle braces. Further, Scala is capable of getting past the JVM limit of type erasure (arguably the worst design choice that Java has) by the use of something called "type manifests". There's special shorthand for this:
scala> def createArray[T: Manifest](size: Int) = new Array[T](size)
createArray: [T](size: Int)(implicit evidence$1: Manifest[T])Array[T]
scala> createArray[Int](5)
res1: Array[Int] = Array(0, 0, 0, 0, 0)
As the echoed declaration reveals, this shorthand is really just asking for an implicit variable of type Manifest[T]
. The user doesn't have to do anything extra because the compiler will supply the appropriate implicit variable.
The Play framework is meant for MVC and thus the layout of the framework assumes you will be using MVC.
It provides a routing file that maps URLs to controllers. Thus, when we visit the URL (with a particular HTTP request type), the Play framework will call the mapped controller (which we can think of as our entry point).
The routing file looks like this:
# The `:id` in the URL means a captured variable
GET /clients/:id controllers.Clients.show(id: Long)
So when you go to /clients/123
for a site with the above routing file, the entry point will be controllers.Clients.show(123)
.
Then we have views (aka templates), which are basically just HTML files in which we can run Scala code in and can pass arguments to (they get compiled down into native Scala functions, but we can treat them as simple HTML templates). Here's a simple view:
@(customer: Customer, orders: Seq[Order])
<h1>Welcome @customer.name!</h1>
<ul>
@orders.map { order =>
<li>@order.title</li>
}
</ul>
The first line is simply an argument list (since these templates work like functions). All the lines that start with @
are calling Scala code. The @customer.name
is simply accessing the name
field of the customer
attribute. The map
call is using a multiline lambda. Note that the lines here are not Scala code, but pure HTML unless we preceed them with @
. Thus, the <li>
tag is simply going to be created verbatim. So if customers.name
was "Mike" and orders
looked something like this (this is JSON, which I assume you can read or can figure out how to read):
[
{ "title": "Foo"},
{ "title": "Bar"}
]
Then the output of the template would be:
<h1>Welcome Mike!</h1>
<ul>
<li>Foo</li>
<li>Bar</li>
</ul>
Anyway, the controller can invoke a view like Ok(views.html.myView(customer, orders)
. The Ok
function is used to create HTTP status codes (eg, 200 = Ok, 404 = not found, etc).
##Actions
The heart of controllers are actions. In fact, each web request should be an action. That is, it returns an Action
. We usually would do this like this:
def index = Action {
println("Lights, camera, action")
Ok("It works!")
}
Note that we kind of treat Action
like it was just a set of braces. In fact, we can really just ignore the fact that we're using an Action
at all and just pretend that we're returning a Request
, which is what Ok
is. What's really happening is that we're executing a block of code that evaluates to a Request
(recall that a block of code always has the value of the last line). This might be clearer if we simplified this code to:
def index = Action(Ok("It works!"))
And that's really all the basics you'd need to create a simple site with Scala and the Play Framework. You'll also probably want to know form handling and accessing a database. Slick provides a cleaner alternative for database interaction (although it has the downside of requiring that we model the database in Scala -- this can be auto-generated, though).
Scala isn't that difficult to go to from Java. Many of the complicated features of the language (which I didn't go into here) don't need to be known for this project. Other features can be learned on-the-fly, when we need them. The language is much less verbose than Java and provides many shortcuts to common programming situations.
Pattern matching and functional programming provide heavy advantages in writing clean code. The very design of functional code makes it easy to test (the Play Framework also has integrated testing).