Mojo
Mojo

Reputation: 1202

Trying To Avoid Having Two Try Blocks Inside My Method

How can I avoid duplicating the if condition in my code:

  def download() =
    Action {
      val out = new FileWriter("/tmp/foo.txt")
      Try {
        val chars = new Array[Char](1024)
        util.Arrays.fill(chars, '.')
        chars(1024) = '\n'
        out.write(chars)
      } match {
        case Failure(_) =>
          if (out != null) out.close()
          println("failed")
        case Success(_) =>
          if (out != null) out.close()
          println("success")
      }
    }

I tried to adapt from the following:

  def download() =
    Action {
      Try {
        val out = new FileWriter("/tmp/foo.txt")
        try{
        val chars = new Array[Char](1024)
        util.Arrays.fill(chars, '.')
        chars(1024) = '\n'
        out.write(chars)
        } if (out != null) out.close()
      } match {
        case Failure(_) =>
          println("failed")
        case Success(_) =>
          println("success")
      }
    }

But that way I have a Java try inside a Scala Try which doesn't seem right. How do I avoid having two try blocks? I basically just want the resource I acquired cleaned up after the method finishes executing.

Upvotes: 0

Views: 79

Answers (3)

Mateusz Kubuszok
Mateusz Kubuszok

Reputation: 27535

If you cannot use Using you can use try-catch-finally:

def download() =
  Action {
    val out = new FileWriter("/tmp/foo.txt")
    try {
      val chars = new Array[Char](1024)
      util.Arrays.fill(chars, '.')
      chars(1024) = '\n'
      out.write(chars)
      println("success")
    } catch {
      case _: Throwable =>
        println("failed")
    } finally {
      out.close()
    }
  }

or loan pattern:

def withFileWriter[A](str: String)(f: FileWriter => A): Try[A] = Try {
  val out = new FileWriter("/tmp/foo.txt")
  try {
    f(out)
  } finally {
    out.close()
  }
}

withFileWriter("/tmp/foo.txt"){ out =>
  util.Arrays.fill(chars, '.')
  chars(1024) = '\n'
  out.write(chars)
} match {
  case Failure(_) =>
    println("failed")
  case Success(_) =>
    println("success")
}

If you have Cats Effect (and if you replace Try with something that is lawful), you can use Resource

val resource = Resource.make {
  IO(new FileWriter("/tmp/foo.txt"))
} { out =>
  IO(out.close())
}

resource.use(out => IO {
  util.Arrays.fill(chars, '.')
  chars(1024) = '\n'
  out.write(chars)
}).attempt.map {
  case Failure(_) =>
    println("failed")
  case Success(_) =>
    println("success")
}.unsafeRunSync

Upvotes: 1

James Whiteley
James Whiteley

Reputation: 3474

Why not just move the out.close() outside of the result of the Try block? If the same check is performed regardless of if it's a success or failure, I don't see the benefit of doing it there. Something like this:

def download() = Action {
  val out = new FileWriter("/tmp/foo.txt")

  Try {
    val chars = new Array[Char](1024)
    util.Arrays.fill(chars, '.')
    chars(1024) = '\n'
    out.write(chars)
  } match {
    case Failure(_) =>
      println("failed")
    case Success(_) =>
      println("success")
  }

  if (out != null) out.close()
}

Upvotes: 1

Ivan Stanislavciuc
Ivan Stanislavciuc

Reputation: 7275

With scala 2.13, you could write the following with the help of scala.util.Using

import scala.util.Failure
import scala.util.Success
import scala.util.Using

Using(new FileWriter("/tmp/foo.txt")) { out =>
  val chars = new Array[Char](1024)
  java.util.Arrays.fill(chars, '.')
  chars(1023) = '\n'
  out.write(chars)
} match {
  case Failure(_) =>
    println("failed")
  case Success(_) =>
    println("success")
}

Upvotes: 1

Related Questions