{{announcement.body}}
{{announcement.title}}

Scala at Light Speed, Part 5: Advanced

DZone 's Guide to

Scala at Light Speed, Part 5: Advanced

Learn more about advanced Scala in the final part of this series!

· Java Zone ·
Free Resource

This article series is for busy programmers who want to learn Scala fast, in 2 hours or less. The articles are the written version of Rock the JVM's Scala at Light Speed mini-course, which you can find for free on YouTube or on the Rock the JVM website in video form.

This is the third article of the series, which will focus on Scala as a functional programming language. You can watch it in video form or in the embedded video below.

So far, we've covered:

  • How to get started with Scala
  • The very basics: values, expressions, types
  • Object orientation: classes, instances, singletons, methods, and basic generics
  • Functional programming
  • Pattern matching

You may also like: You may also like: Scala at the Speed of Light, Part 1: The Essentials,  Part 2: Object OrientationPart 3: Functional Programming, Part 4: Pattern Matching

Lazy Evaluation

In plain language, a lazy value is only evaluated at first use.

Scala


Lazy evaluation is particularly useful when this value takes a long time to compute: you don't need to stall the entire code, you'll only do that when you need the value. Just to prove this, let's compute a value with a side effect, like printing something to the console.

Scala


If you put this in a standalone application and run it without using the value, nothing will show up - that's because the value is not evaluated. If, instead, you add:

Scala


And run the application again, the string I am so very lazy! will suddenly show up in the application again. (example is most visible on the video)

Lazy values are useful when operating on infinite collections like Streams.

Option

Think of an Option as a "collection" containing at most one element. Options were created to avoid needing to work with nulls, which causes so much headache in the Java world and so much defensiveness in production code.

Scala


When you have an unsafe method that might return null, you can make it safe by wrapping a call into an Option. The option will be one of two subtypes:

  • Some(value), which is a case class
  • None, which is an object

We can pattern match Options very easily to obtain the values inside. More importantly, Options work in the same fashion as Lists do, so we can compose Options with map, flatMap, filter or for-comprehensions as we did with lists. This is the functional style of thinking, where we compose values (in this case Options) to obtain other values.

Try

Try works in a similar way, in that you can think of it like a "collection" containing at most one element. A Try is used when the code you want to evaluate might throw an exception. In the Java world, this causes either lots of runtime crashes or a barrage of try-catch defensiveness.

Scala


A Try instance can either be:

  • a Success containing a real value which you can then use
  • or a Failure containing an exception which you can then process

Notice that instead of crashing an application or throwing an exception (which is a side effect), we're instead wrapping the value or the exception in a small data structure (we're describing the side effect if it happens).

Future

Futures were created for asynchronous computation, i.e. computation that happens on some thread you have no control over. In a similar fashion, we can think of a Future as a "collection" containing an element. This time, a Future only contains an element when the computation of the value finishes on the thread it runs. 

Scala


Two notes on the above code:

  • code style: you can eliminate the parentheses in the Future constructor when a code block is the argument; parentheses showed for consistency with the basic syntax used so far
  • notice the import at the top; to run a Future, you need an ExecutionContext (= a thread pool), which is why we import a standard one

In this case, there's no way to pattern match a future because you don't know when it will finish. Instead, we usually compose Futures with map, flatMap, and filter, much like we did with lists.

A Soft Intro to Implicits

This is probably the most powerful feature of the Scala compiler. It's so powerful it's often dangerous, which is why the upcoming Scala 3 is going to change the language a lot around this area. It will still be supported but deprecated/dropped at some point (perhaps in 3.1 or later).

Just to test the waters on implicits, we're going to exemplify two use cases for implicits.

Use-case 1: implicit arguments. A method taking an implicit argument list will need the compiler to automatically fetch a value to inject for those arguments. Example:

Scala


Notice that the method takes an implicit Int as an argument. Given that I've defined an implicit Int before I call the method, the compiler is happy to inject that value into the method call, so I don't need to provide the arguments. This same mechanism happened with the Future constructor earlier (which is why we imported the global value). There must only be one implicit value of a given type in the same scope. Otherwise the compiler will complain, because it won't know which value you'd like to use.

Use-case 2: implicit conversions. This is an enormous extension power of the Scala language, but comes with dangers. So use this feature as in the following example:

Scala




xxxxxxxxxx
1


 
1
  implicit class MyRichInteger(n: Int) {
2
    def isEven() = n % 2 == 0
3
  }
4
 
          
5
  println(23.isEven()) // new MyRichInteger(23).isEven()
6
  // use this carefully


An implicit class can only take one argument. That's for a very good reason: after you make the implicit class available (by either writing it, or importing it) in your scope, the compiler is free to auto-wrap Ints into instances of your implicit class. In regular code, you write 23.isEven as if isEven were a method on the Int type, but in reality, the compiler auto-boxes the number into an implicit class. You can define as many implicit classes you like, as long as you don't have method conflicts.

Implicits bring Scala's type system to a whole new level, as they allow extending types from libraries you don't have any control over, or even standard types like Int or String (as the above example). The unofficial term is "pimping" a library, the technical term is "type enrichment" - use whichever you like.

This was Scala at Light Speed. There is much, much more to Scala, but I hope this article series was fun and valuable!

- Daniel for Rock the JVM

Further Reading

Clean Code Best Practices in Scala

A Journey With Scala

[DZone Refcard] Getting Started With Scala

Topics:
fp, functional programming, java, scala, tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}