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 the Try is successful in this example, the JSON will be passed as a parameter into the JsValue -> Future[Result] block to later be converted from JSON into a Future[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 a String 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 a Left

    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 in for comprehensions

    Another way to see if there is a Left in your results is to use a for comprehension to handle each value.

    The for comprehension will fall over at the line containing the Left, 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 a Seq:

    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 separate val).

    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 a List[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 for List(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!