DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Scala at Light Speed, Part 1: The Essentials
  • Thermometer Continuation in Scala
  • Deploying a Scala Play Application to Heroku: A Step-by-Step Guide
  • Upgrading Spark Pipelines Code: A Comprehensive Guide

Trending

  • Segmentation Violation and How Rust Helps Overcome It
  • Optimize Deployment Pipelines for Speed, Security and Seamless Automation
  • How to Configure and Customize the Go SDK for Azure Cosmos DB
  • A Complete Guide to Modern AI Developer Tools
  1. DZone
  2. Coding
  3. Languages
  4. Scala at Light Speed, Part 5: Advanced

Scala at Light Speed, Part 5: Advanced

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

By 
Daniel Ciocirlan user avatar
Daniel Ciocirlan
·
Jan. 13, 20 · Tutorial
Likes (5)
Comment
Save
Tweet
Share
13.1K Views

Join the DZone community and get the full member experience.

Join For Free

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


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 fifth 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

Lazy Evaluation

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

Scala
 




xxxxxxxxxx
1


 
1
lazy val aLazyValue = 2



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
 




xxxxxxxxxx
1


 
1
  lazy val lazyValueWithSideEffect = {
2
    println("I am so very lazy!")
3
    43
4
  }



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
 




xxxxxxxxxx
1


 
1
  val eagerValue = lazyValueWithSideEffect + 1



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
 




xxxxxxxxxx
1


 
1
  def methodWhichCanReturnNull(): String = "hello, Scala"
2
  val anOption = Option(methodWhichCanReturnNull()) // Some("hello, Scala")
3
  // option = "collection" which contains at most one element: Some(value) or None
4

          
5
  val stringProcessing = anOption match {
6
    case Some(string) => s"I have obtained a valid string: $string"
7
    case None => "I obtained nothing"
8
  }



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
 




xxxxxxxxxx
1


 
1
  def methodWhichCanThrowException(): String = throw new RuntimeException
2
  val aTry = Try(methodWhichCanThrowException())
3
  // a try = "collection" with either a value if the code went well, or an exception if the code threw one
4

          
5
  val anotherStringProcessing = aTry match {
6
    case Success(validValue) => s"I have obtained a valid string: $validValue"
7
    case Failure(ex) => s"I have obtained an exception: $ex"
8
  }



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
 




xxxxxxxxxx
1


 
1
import scala.concurrent.ExecutionContext.Implicits.global
2

          
3
val aFuture = Future({
4
  println("Loading...")
5
  Thread.sleep(1000)
6
  println("I have computed a value.")
7
  67
8
})



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 that 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
 




xxxxxxxxxx
1


 
1
  def aMethodWithImplicitArgs(implicit arg: Int) = arg + 1
2
  
3
  implicit val myImplicitInt = 46
4
  println(aMethodWithImplicitArgs)  // aMethodWithImplicitArgs(myImplicitInt)



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.

So, that's a wrap for 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

[DZone Refcard] Getting Started With Scala

Scala (programming language) Light (web browser)

Opinions expressed by DZone contributors are their own.

Related

  • Scala at Light Speed, Part 1: The Essentials
  • Thermometer Continuation in Scala
  • Deploying a Scala Play Application to Heroku: A Step-by-Step Guide
  • Upgrading Spark Pipelines Code: A Comprehensive Guide

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!