In this post, I'm going to attempt to explain concepts that are a bit more advanced. Some have .NET equivalents, others don't. Don't sweat it if you don't fully grasp some of the new Scala only concepts, these will be reinforced in future posts.
Immutable by default
This can be a fairly jarring paradigm shift, but Scala is immutable by default. Instead of creating variables that can be reassigned at any time, you typically create variables that are equivalent to using the const keyword in C#:
This means you will not be able to reassign this variable in the future. You would either have to create a new list (preferred) or use the var keyword.
This concept extends further, however. Because this is a design goal, the standard Scala library does not provide mutable collections by default. For example, if you wanted to use a list and append an item, you would find that there is no append method. Instead, you could use the +: method to create a copy of that list with the item appended to the end.
There is a set of mutable collections that are provided, as the example shows, but this is not usually the desired solution unless you need the performance.
Traits can have bodies
Traits are Scala's answer to interfaces, which means you can implement multiple traits on a class. One major difference is that they can actually include method bodies, including methods with non-public visibility. You can almost think of them like an abstract class, except they don't have a constructor.
Importing Objects (Similar to using static in C# 6.0)
Similarly, in Scala, you can import all methods from an object (Scala's version of a static class by using a singleton).
The difference is that Scala uses an underscore after the object name to indicate that you want to use all methods.
Coming from the .NET world, dependency injection is the best use case that made this concept click for me. You can think of implicit parameters as Scala's baked in answer to an IoC container.
In .NET, you'd tend to pass dependencies to your constructor to mock them out, change the implementation, and hide the implementation details from the caller. For example, you may have a repository class in .NET:
This is nicely encapsulated so the caller has no idea how the Person instance is persisted. It also allows mocking through constructor injection. Typically in the .NET world, you'd use a DI framework like Castle Windsor, Autofac or Ninject to manage all of your dependencies. You could do it manually, but it quickly becomes very painful, especially when refactoring.
In Scala, you can use implicit parameters and objects to achieve the same effect as an IoC container. By importing all implicit parameters in an object, you can instantiate a class without explicitly providing constructor parameters:
The complier injects the dependencies automatically (note that it matches on the instance type *not* the variable names), which would look like this if you were to handle the injection manually:
What's the advantage of using implicit assignment? It's essentially the same as using a DI container - when you refactor, you don't have to change nearly as much code. If you reorder parameters or remove them all together, your code will still compile. One nice difference is when you add a parameter that is not implicitly available in scope, the compiler will throw an error. IoC containers in .NET often don't catch this mistake until runtime.
One disadvantage to constructor injection is that it assumes each method will actually make use of all the dependencies passed in, which is rarely the case. Scala allows you to use implicit parameters on methods as well, so you can be sure you created a dependency because you're actually going to use it:
Note that none of the calling code had to change after refactoring the PersonRepository. This refactoring is nice because it allows you to call the getCached method without providing a SqlConnection instance - this simplifies the call and reduces the resources used when calling only the getCached method.
Because you can always explicitly provide a parameter, you get all the benefits of DI without needing to use a framework. By using implicit parameters, you can safely refactor and test your objects, traits and classes with minimal effort.
I'm sure these are familiar to .NET devs. Scala allows you to define similar extensions by using implicit classes inside of an object.
Scala tries to be more concise by creating some shortcuts in the syntax. I personally found most of these things to be quite logical once I understood the concept, but they may not be completely straightforward the first time you see them.
You can almost think of these as POCO classes in .NET - although just like in .NET, they are capable of having methods.
There are three major differences between a case class and a regular class. First, you do not have to use the new keyword to instantiate them. You can just use the class name:
Secondly, the parameter arguments are automatically exposed as public fields:
Lastly, the equality operator works by comparing the fields themselves, rather than the object reference:
Tuples are available in .NET, but aren't overwhelmingly popular. Probably for good reason, while they are flexible and capable of carrying related pieces of information without declaring a class, they make code more difficult to read, .
Scala has tuples baked into the language, you can return a tuple by simply surrounding your instances in parenthesis:
It's certainly a nice feature to have available, but code can become nearly unreadable if they are overused. If you use a tuple and run into any readability issues, try using a case class instead.
Symbols for Method Names
Note how this can make code fairly tricky to read. Keep in mind that if you see a symbol you don't recognize, it very well may be part of a custom DSL, either imported from an object (like the example above) or inherited from a trait. Using your IDE to go to the method declaration is a really handy way to figure out what's going on.
This is slightly tricky because of the way Scala treats the order of operations, but parenthesis are not always required and can instead be substituted with spaces:
This is another piece of the puzzle when reading Scala code. Keep in mind that spaces and symbols can make nice looking DSLs, but they can also be highly confusing when first learning.
Until next time
That's certainly not everything, but if you understand most of these concepts, you can hopefully start looking at some code examples without being thoroughly confused (as I was). Next time we'll talk about Common Data Types in Part 3. Thanks for reading!
I'm currently accepting new clients and projects! If you need an experienced Software Developer, please contact me.