Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to ensure a resource is closed in a for-comprehension in Scala

Tags:

scala

How is functions with side-effects best handled in for-comprehensions in Scala?

I have a for comprehension that starts by creating a kind of resource (x) by calling a function f1. This resource has a close-method that needs to be called at the end but also if the for-comprehension fails somehow (unless.

So we have something like:

import scala.util.{Try,Success,Failure}

trait Resource {
  def close() : Unit
}

// Opens some resource and returns it as Success or returns Failure
def f1 : Try[Resource] = ...
def f2 : Try[Resource] = ...

val res = for {
  x <- f1
  y <- f2
} yield {
  (x,y)
}

Where should I call the close method? I can call it at the end of the for-comprehension as the last statement (z <- x.close), in the yield-part, or after the for-comprehension (res._1.close). None of them ensures that close is called if an error occurs (e.g. if f2 fails). Alternatively, I could separate

x <- f1 

out of the for-comprehension like this:

val res = f1
res match {
  case Success(x) => {
    for {
      y <- f2
    }
    x.close
  }

  case Failure(e) => ...       
:

That would ensure the call of close but is not very nice code. Is there not a smarter and more clean way to achieve the same?

like image 760
Asger Eir Avatar asked Dec 14 '22 12:12

Asger Eir


2 Answers

When I have such problem I decide between 2 possibilities:

  1. Use Scala ARM
  2. Implement Loan Pattern on my own (link is volatile and could die)

In most cases I prefer own implementation to avoid additional dependency. Here is the code of Loan Pattern:

def using[A](r : Resource)(f : Resource => A) : A =
    try {
        f(r)
    } finally {
        r.close()
    }

Usage:

using(getResource())(r =>
    useResource(r)
)

Since you need 2 resources you will need to use this pattern twice:

using(getResource1())(r1 =>
    using(getResource2())(r2 =>
        doYourWork(r1, r2)))

You can also look on following answers:

  • Scala: Disposable Resource Pattern
  • functional try & catch w/ Scala
  • Using a variable in finally block
like image 177
Oleg Rudenko Avatar answered May 14 '23 17:05

Oleg Rudenko


A common pattern for closing resources is the loan pattern:

type Closable = { def close(): Unit }

def withClosable[B](closable: Closable)(op: Closable => B): B = {
  try {
    op(closable)
  } finally {
    closable.close()
  }
}

With a little refactoring you can use this pattern:

import scala.util.{Try,Success,Failure}

trait Resource {
  def close() : Unit
}

// Opens some resource and returns it as Success or returns Failure
def f1(res: Resource) : Try[Resource] = ???
def f2(res: Resource) : Try[Resource] = ???

val f1Resource: Resource = ???
val f2Resource: Resource = ???

val res = for {
  x <- withClosable(f1Resource)(f1)
  y <- withClosable(f2Resource)(f2)
} yield {
  (x,y)
}

or

import scala.util.{Try,Success,Failure}

trait Resource {
  def close() : Unit
}

// Opens some resource and returns it as Success or returns Failure
def f1: Try[Resource] = { 
  val res: Resource = ???
  withClosable(res){ ... }
}
def f2: Try[Resource] = { 
  val res: Resource = ???
  withClosable(res){ ... }
}

val res = for {
  x <- f1
  y <- f2
} yield {
  (x,y)
}
like image 36
Sascha Kolberg Avatar answered May 14 '23 17:05

Sascha Kolberg