SCALA DUMP ZONE
Helper page for Scala snippets
-
Passing a function as a curried parameter to create an onSuccess block
You can turn a function into a Higher-Order Function and curry your parameters in order to avoid duplicated code, and to make code blocks more human-readable through abstracting methods which might otherwise look cumbersome.
Example: with Scala Play, turning a POST request's body into JSON and custom-handling any errors with a
Try
block. If theTry
is successful in this example, the JSON will be passed as a parameter into theJsValue -> Future[Result]
block to later be converted from JSON into aFuture[Result]
.def requestToJson(request: Request[AnyContent])(onSuccess: => JsValue => Future[Result]): Future[Result] = { Try(request.body.asJson.get) match { case Success(json) => onSuccess(json) case Failure(_) => Future.successful(UnsupportedMediaType("This is not JSON...")) } }
Use:
def handleGet(): Action[AnyContent] = Action.async(parse.anyContent) { implicit request => requestToJson(request) { json => Future.successful(Ok(json)) } }
-
Catching exceptions with Either[Left, Right]
Use similar to
Option
Example, storing either a
Throwable
or aString
as a form of validation:scala> def isCorrectString(input: String): Either[Throwable, String] = { | if(input == "I match perfectly") Right("IT'S A MATCH") | else Left(new Exception("It didn't match...")) | } isCorrectString: (input: String)Either[Throwable,String] scala> isCorrectString("I match perfectly") res0: Either[Throwable,String] = Right(IT'S A MATCH) scala> isCorrectString("I do not match perfectly") res1: Either[Throwable,String] = Left(java.lang.Exception: It didn't match...)
Checking if a
Collection
contains aLeft
An example of where this can be useful is if you are making multiple POST requests to an API and want to verify that they have all passed.
You would do this by checking if a Collection contains any
Left
values with.exists(_.isLeft)
.scala> val seq: Seq[Either[Int, String]] = Seq(Right("string"), Right("string"), Left(3), Right("string")) seq: Seq[Either[Int,String]] = List(Right(string), Right(string), Left(3), Right(string)) scala> seq.exists(_.isLeft) res0: Boolean = true scala> val seq2: Seq[Either[Int, String]] = Seq(Right("string"), Right("string"), Right("string")) seq2: Seq[Either[Int,String]] = List(Right(string), Right(string), Right(string)) scala> seq2.exists(_.isLeft) res1: Boolean = false
Either
infor
comprehensionsAnother way to see if there is a
Left
in your results is to use afor
comprehension to handle each value.The
for
comprehension will fall over at the line containing theLeft
, and will subsequently not analyse the rest of the comprehension.This is helpful if you want to capture at which point an error occurred when debugging your code.
Setup:
scala> case class Error(msg: String) defined class Error scala> type Validation[+A] = Either[Error, A] defined type alias Validation scala> val result1: Validation[Int] = Right(42) result1: Validation[Int] = Right(42) scala> val result2: Validation[String] = Right("Is the Answer") result2: Validation[String] = Right(Is the Answer) scala> val result3: Validation[Int] = Left(Error("Ooops Failed")) result3: Validation[Int] = Left(Error(Ooops Failed))
Putting each
Right
in aSeq
:scala> val response = for { | res1 <- result1.right | res2 <- result2.right | res3 <- result3.right | } yield { | Seq(res1, res2, res3) | } response: scala.util.Either[Error,Seq[Any]] = Left(Error(Ooops Failed))
The comprehension even falls over if you don't end up using the
Left
value:scala> val response2 = for { | res1 <- result1.right | res2 <- result2.right | res3 <- result3.right | } yield { | Seq(res1, res2) | } response2: scala.util.Either[Error,Seq[Any]] = Left(Error(Ooops Failed))
The comprehension will pass if all of the values are
Right
:scala> val respose3 = for { | res1 <- result1.right | res2 <- result2.right | } yield { | Seq(res1, res2) | } respose3: scala.util.Either[Error,Seq[Any]] = Right(List(42, Is the Answer))
-
Using apply methods to call different functions by default
Say you are adding logging to an application, and you are using the following class to format it:
scala> final case class LogMessage(keyValuePairs: (String, String)*) { | def jsonify: String = | s"""{${keyValuePairs map { case (key, value) => s""""$key":"$value"""" } mkString "," }}""" | } defined class LogMessage scala> val log: String = LogMessage(("message1", "my first message"), ("message2", "my second message")).jsonify log: String = {"message1":"my first message","message2":"my second message"}
Instead of needing to call
.jsonify
every time you create a new LogMessage, you can create an object with an.apply
method to handle this for you:scala> object LogMessage { | def apply(keyValuePairs: (String, Any)*): String = { | val jsonified = keyValuePairs.map { case (key, value) => s""""$key":"$value"""" }.mkString(",") | s"{${jsonified}}" | } | } defined object LogMessage ^ scala> val log2: String = LogMessage(("message1", "my first message"), ("message2", "my second message")) log2: String = {"message1":"my first message","message2":"my second message"}
More than one Apply method can be created too. Apply methods can really clean up your code nicely if used correctly. Below is a (rather trivial) example of an object containing multiple
.apply()
methods:scala> object TypeGuesser { | def apply(string: String): Right[String, String] = { | Right("I guess... String!") | } | | def apply(int: Int): Left[String, String] = { | Left("I guess... Int!") | } | } defined object TypeGuesser scala> TypeGuesser("my string") res0: Right[String,String] = Right(I guess... String!) scala> TypeGuesser(123) res1: Left[String,String] = Left(I guess... Int!)
-
Utilising functions in implicit classes to make your code look nice
If you make a function which multiplies a number by two, it can look pretty ugly:
scala> val number = 4 number: Int = 4 scala> def timesTwo(num: Int): Int = num * 2 timesTwo: (num: Int)Int scala> timesTwo(number) res0: Int = 8
This can look much nicer and more human-readable though, with the use of implicit classes:
scala> implicit class NumberManipulation(num: Int) { | def timesTwo: Int = num * 2 | } defined class NumberManipulation scala> number.timesTwo res2: Int = 8
These functions can be stacked too:
scala> implicit class NumberManipulation(num: Int) { | def timesTwo: Int = num * 2 | def addThree: Int = num + 3 | def squared: Int = num * num | } defined class NumberManipulation scala> val number = 4 number: Int = 4 scala> number.timesTwo.addThree.squared res1: Int = 121
Obviously, this can be a lot more powerful than just number manipulation but this demonstrates how more readable this is than defining the functions separately and writing
squared(addThree(timesTwo(number)))
(or assigning each step to a separateval
).Note: the Implicit Class must be in-scope in order for this to work - you can get around this by doing something like importing it into scope. They must also always be inside another
trait
/class
/object
, and can only take one non-implicit argument in their constructor. -
Type erasure is where List[String] == List[Int] at runtime with the JVM. Here is how to get around that...
With the following code, the final
case _
will never be hit:import play.api.libs.json._ def matchThis(list: List[JsValue]): Any = { list match { case value: List[JsObject] => "ABC" case _ => 123 } } val list = List(Json.obj()) matchThis(list) // res0: Any = ABC val list2 = List(JsString("123213")) matchThis(list2) // res1: Any = ABC
This is because Scala runs using the JVM, and at runtime the JVM will only see that it is matching against
List[ANYTHING]
and will succeed even when passed aList[JsString]
. The JVM ignores Generics at runtime.To get around this, you can use
scala.reflect.runtime.universe
and TypeTags to match against types:import scala.reflect.runtime.universe._ import play.api.libs.json._ def matchThis[A: TypeTag](list: List[A]) = list match { case value: List[JsObject @unchecked] if typeOf[A] =:= typeOf[JsObject] => "ABC" case _ => 123 } val list = List(Json.obj()) matchThis(list) // res0: Any = ABC val list2 = List(JsString("123213")) matchThis(list2) // res1: Any = 123 val list3 = List(Json.obj(), Json.obj()) matchThis(list3) // res2: Any = ABC val list4 = List(JsString("123213"), JsString("abcabcabcabca")) matchThis(list4) // res3: Any = 123
This example uses PlayFramework's JSON case classes to match but obviously can be applied to any class/type/whatever comparison.
You can also use ClassTag to achieve a similar effect - example below copied from here. See link for more info.
import scala.reflect.{ClassTag, classTag} def matchList[A : ClassTag](list: List[A]) = list match { case strlist: List[String @unchecked] if classTag[A] == classTag[String] => println("A List of strings!") case intlist: List[Int @unchecked] if classTag[A] == classTag[Int] => println("A list of ints!") }
-
Do not mix returns!
for (a <- List(1, 2, 3) yield a * a
is just syntactic sugar forList(1, 2, 3).map(a => a * a)
. You cannot do something like this:import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global for { a <- Future("hi") b <- List(1, 2, 3) } yield (a, b)
List != Future!