Tuesday, September 22, 2015

Scala for the C# Web Developer: Part 3 - Common Data Types and Pattern Matching

I'm currently accepting new clients and projects! If you need an experienced Software Developer, please contact me.

In Part 1, we discussed some feature similarities that Scala and C# have in common to gain some familiarity with the language. Part 2 introduced both new and familiar language features that may or may not be familiar to C# developers.

In this part, we're going to talk about a couple of commonly used data types (the List and Option types), as well as pattern matching (a better, more readable alternative to an if/else statement).

Lists

The List is one of the more familiar data types in Scala. The syntax is slightly different, but overall the API is very similar to .NET.

Instantiation

You can create a populated list in one line:

Note that you do not need to declare the list type - it will be inferred by the items that are added. This is the equivalent of declaring the arguments explicitly:

Filter - C# Where()

Just like in C#, you can filter a list by any criteria:

Head - C# First()

Often you'll just want to grab the first item in a list:

In C#, it's pretty common to grab the first item that matches a specific criteria. In Scala, there isn't a function specifically for this purpose. Instead you'd just chain filter with the head function.

A few more mappings

I'm not going to go any further here - there hasn't been a function in C# that I haven't been able to find an equivalent for in Scala. Instead, here are a few more mappings, which I'm sure you'll be able to figure out without much hassle:

C# Scala
Count() count() or length
Select() map()
SelectMany() flatMap()
Skip() drop()
Take() take()
ForEach() forEach()
Any() nonEmpty or exists()
All() forall()
OrderBy() sortBy()

Full reference can be found in the Scala documentation. Note that you may want to check out Seq, Iterable, and any other classes in the inheritance chain for more restricted collection implementations. A List isn't always the right structure, but it's a good place to start.

Option - Better C# Nullable

At first, I thought that this would be a new concept, but then I remembered that we have a pretty comparable concept (Nullable<T>) in C#. The big difference is that the Option type works on all instances in Scala, and they should always be used in situations where a value would traditionally return null. This allows you to avoid the dreaded NullReferenceException/NullPointerException by prompting you to deal with both null and non-null cases when attempting to access the value.

You can create an optional value by wrapping it:

You can also wrap a null value:

Of course, these examples aren't very realistic, since you wouldn't typically wrap a value like this if you already know it's null or not. More realistically, you would wrap a value when you're calling a code that could return null (typically Java library calls – Scala libraries shouldn't return null):

Usage

Now that you have an Option instance, you'll likely want to transform it into something usable. If we were using .NET conventions, we would use an if/else statement:

Map/GetOrElse

In Scala, there are several ways to accomplish the same task. Most commonly, I use map in conjunction with the getOrElse method:

This example may be easier to conceptualize if you think of an Option instance as a list that is restricted to holding a maximum of one item. We're mapping a result, but that code will only be executed if there is an item in the list. Then getOrElse will do the inverse - it will only be executed if the Option instance is empty. We then surround the result in a print statement, and since both paths return a string, we'll get the same output as the C# styled example.

Also note that the map method is allowing us to transform the underlying Option instance from an Employee to a String, just as you can do with the Select method in .NET.

Fold

The fold method is actually even better suited to this particular task, although I don't find myself using it quite as often:

The first set of parenthesis takes one argument, which is the result if the value is empty. The second set handles non-null values, and passes the underlying value via lambda.

Pattern Matching - Better Switch Statements

In C#, I rarely ever use a switch statement, mainly because their usage is restricted to integral types only. If you want to do anything remotely complex, you'd typically have to use any number of if statements.

Pattern matching in Scala is what switch statements wish they could be. You can match on a variety of types and values.

Match by Class

Since we just talked about the Option type, we can start there. Pattern matching is yet another way the above print statement could be written:

Note that the above expression could instead be wrapped with the print() method as in previous examples - recall that everything returns a value in Scala.

Some and None are the underlying case classes that are returned when you wrap a value with Option(...). In the above example, we're asking it to match on whichever type was returned and to do something in each case.

  • If the value is non empty (an instance of Some), name the underlying value e, then print the full name.
  • If the value is empty (an instance of None), print that no employee was found.

Narrowing with if statements

You can further filter your matches with if statements:

In the above example, we're checking to see if our list is a certain length. If it's not, we can use the fall through case _ to handle a scenario we're not specifically checking for. Alternatively, you can use any variable name if you'd like to reference the value in your case statement body. It's important to include this if you have not exhaustively checked all scenarios, or else your code may throw an exception.

Case Classes in Pattern Matching

Recall that I mentioned Some and None are case classes. There is another special attribute of case classes that I didn't mention previously – their constructor arguments can be used in pattern matching. If we use our Person case class from above, we could combine the concepts from our previous examples to check if a Person is over the age of 21:

Advanced Usages

There are many more advanced usages that I won't be covering here. The most important thing to remember is that certain types will pass back underlying implementations that you can pattern match on, such as Option returning Some or None (another one that comes to mind is Either, which can return Left or Right). Keep an eye out for this when working in Scala, and you should be able to piece together the intended design of the data type.

Until next time

That wraps up the language comparison for now. While Scala does provide many, many more features and data constructs, these are the most common concepts that I've encountered while creating web projects, and hopefully will be enough to get you comfortable writing some code. Next time, we'll discuss how to create a web project in Part 4: Development Environment.

I'm currently accepting new clients and projects! If you need an experienced Software Developer, please contact me.

0 comments:

Post a Comment