The loan pattern is more usual for this use case, but since anything goes on Stack Overflow, you can construct the expression you're looking for with Try
.
Try
deserves more exposure as a handy tool.
scala> import util._
import util._
scala> import io._
import io._
Try to open the file --
scala> def f =
| Try (Source.fromFile("foo.text")) map { in =>
then do something with it, packaging the result in a tuple with the i/o source --
note that when you do a value definition in a for-comprehension, this is what it does --
| (in, Try(in.getLines.mkString("/")))
| } flatMap {
then close the source and yield the result of the computation --
| case (in, res) =>
| in.close()
| res
| }
f: scala.util.Try[String]
Uncommented:
scala> def f =
| Try (Source.fromFile("foo.text")) map { in =>
| (in, Try(in.getLines.mkString("/")))
| } flatMap {
| case (in, res) =>
| in.close()
| res
| }
f: scala.util.Try[String]
scala> f
res1: scala.util.Try[String] = Failure(java.io.FileNotFoundException: foo.text (No such file or directory))
Create the test file with some classic humour text, then try again:
scala> f
res2: scala.util.Try[String] = Success(Now is the time/for all good dogs/to lie.)
You can sugarcoat it as a for-comprehension, though observe the extra flatten, since you get a map instead of flatMap from the yield:
scala> def g = (for {
| in <- Try (Source.fromFile("foo.text"))
| res = Try(in.getLines.mkString("/"))
| } yield {
| in.close()
| res
| }).flatten
g: scala.util.Try[String]
scala> g
res2: scala.util.Try[String] = Success(Now is the time/for all good dogs/to lie.)
What if we want to fail if the close fails?
I don't want to type in all that stuff into the REPL again!
scala> :hi // :history
[snip]
2490 def g = (for {
2491 in <- Try (Source.fromFile("foo.text"))
2492 res = Try(in.getLines.mkString("/"))
2493 } yield {
2494 in.close()
2495 res
2496 }).flatten
2497 :hi
scala> :edit 2490+7 // or just :edit 2490-
+import util._
+import io._
+def g = (for {
+ in <- Try (Source.fromFile("foo.text"))
+ res = Try(in.getLines.mkString("/"))
+} yield {
+ val ok = Try(in.close())
+ res transform (s => ok map (_ => s), new Failure(_))
+}).flatten
+
import util._
import io._
g: scala.util.Try[String]
The transform
says that if the computation succeeded, convert that success to failure if the result of the close, ok
, is a failure; and on a failed computation, keep that failure, though some people prefer to add up their failures.
Don't you know try/catch is so 1990s. </droll> (droll does not mean troll.)