Skip to content

Instantly share code, notes, and snippets.

@robinp
Created May 16, 2012 11:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robinp/2709535 to your computer and use it in GitHub Desktop.
Save robinp/2709535 to your computer and use it in GitHub Desktop.
How to close safely using Scalaz IO
import java.io.{IOException, InputStream, OutputStream}
import org.mockito.Mockito
import scalaz._
import Scalaz._
import scalaz.effects._
trait Closable[A] {
def close(a: A): IO[Unit]
def closeOrKeep(a: A): IO[Option[A]] =
close(a) map (_ => none[A]) except (e => io(some(a)))
}
trait StreamClosables {
implicit object OutputStreamIsClosable extends Closable[OutputStream] {
def close(a: OutputStream) = io(a.close)
}
implicit object InputStreamIsClosable extends Closable[InputStream] {
def close(a: InputStream) = io(a.close)
}
}
trait IOUtils {
/**
* Executes the action described by `f`.
* Any exceptions are caught.
*
* In the returned pair:
* Some(a) is returned if `a` could not be closed despite a close attempt.
* None[A] if `a` was closed.
* Some(b) if the result is available (even though closing may have not succeeded).
* None[B] if there was an exception while getting the result.
*/
def executeAndClose[A : Closable, B](a: A, f: A => IO[B]): IO[(Option[A], Option[B])] = {
val tryClose = implicitly[Closable[A]].closeOrKeep _
val doAction =
for {
b <- f(a) // can except
keptA <- tryClose(a)
}
yield (keptA, some(b))
doAction except (e =>
for {
keptA <- tryClose(a)
}
yield (keptA -> none[B]))
}
}
object IOTesting extends IOUtils with StreamClosables {
def main(args: Array[String]) {
val fineClosingIstream = Mockito mock classOf[InputStream]
val badlyClosingIstream = Mockito mock classOf[InputStream]
Mockito when badlyClosingIstream.close thenThrow new IOException("i am bad")
def goodProcessing(istream: InputStream): IO[String] = io("Text read")
def badProcessing(istream: InputStream): IO[String] = io(throw new IOException("failed read"))
println(executeAndClose(fineClosingIstream, goodProcessing _).unsafePerformIO)
println(executeAndClose(fineClosingIstream, badProcessing _).unsafePerformIO)
println(executeAndClose(badlyClosingIstream, goodProcessing _).unsafePerformIO)
println(executeAndClose(badlyClosingIstream, badProcessing _).unsafePerformIO)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment