Search code examples
scalapattern-matchinglazy-initialization

How can I do pattern matching on tuple with lazy initialization?


I have an scenario where I need to call up to three services to do something. Each service has some kind of priority and my algorithm depends on the combination of the result of each service (all of them, two or even one). In order to handle this situation I want to use pattern matching (because the matchs, and variable extraction)

Here you are a simplified example.

case class Foo(bar: String, baz: Option[String])

def expensiveOperation1(): String = ???
def expensiveOperation2(): List[Int] = ???
def expensiveOperation3(): Foo = ???

lazy val r1 = expensiveOperation1()
lazy val r2 = expensiveOperation2()
lazy val r3 = expensiveOperation3()

(r1, r2, r3) match {
  case ("Value1", _, _)          => "1"
  case ("Value2", _, _)          => "2"
  case (_, List(1), _)           => "3"
  case (_, Nil, _)               => "4"
  case ("Value3", 1 :: tail, _)  => "5" + tail
  case (_, _, Foo("x", Some(x))) => x
  case (_, _, _)                 => "7"
}

As you can see, there is no need to call expensiveOperation2 and expensiveOperation3 all the time, but though I save each result on lazy vals, in the moment when I create Tuple3, each method is called.

I could create a container LazyTuple3 with three params call by name in order to solve that problem, but I'll get a new problem, unapply method (LazyTuple3.unapply) returns an Option, so after the first "case" each method will be called.

I could solve this with nested "if" or "match" but I want to give a chance with one "match", I find it clearer.

Any idea?

Thanks in advance.


Solution

  • Try to use scalaz.Need. https://static.javadoc.io/org.scalaz/scalaz_2.12/7.2.26/scalaz/Need.html

    case class Foo(bar: String, baz: Option[String])
    
    def expensiveOperation1(): String = {
        println("operation1")
        "Value3"
    }
    def expensiveOperation2(): List[Int] = {
        println("operation2")
        List(1, 2, 3)
    }
    def expensiveOperation3(): Foo = {
        println("operation3")
        Foo("x", Some("x"))
    }
    
    lazy val r1 = Need(expensiveOperation1())
    lazy val r2 = Need(expensiveOperation2())
    lazy val r3 = Need(expensiveOperation3())
    
    (r1, r2, r3) match {
        case (Need("Value1"), _, _)                => "1"
        case (Need("Value2"), _, _)                => "2"
        case (_, Need(List(1)), _)                 => "3"
        case (_, Need(Nil), _)                     => "4"
        case (Need("Value3"), Need(1 :: tail), _)  => "5" + tail
        case (_, _, Need(Foo("x", Some(x))))       => x
        case (_, _, _)                             => "7"
    }
    

    This will print:

        operation1
        operation2