Skip to content

Instantly share code, notes, and snippets.

@KatrinaHoffert
Last active August 29, 2015 14:13
Show Gist options
  • Save KatrinaHoffert/ff14652c9e0a24fa86bd to your computer and use it in GitHub Desktop.
Save KatrinaHoffert/ff14652c9e0a24fa86bd to your computer and use it in GitHub Desktop.
Stop making shelves and start making unicorns -- putting the "fun" in "functional"

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.

Types and variables

Types like those in Java

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)

New types and stuff

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 Options: 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)

Control structures

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 Trys. 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).

Classes and objects

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

Inheritance

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.

Functional programming

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)

Misc cool stuff

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 Objects. 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.

Play framework

Architecture

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).

TL;DR

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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment