public
Created

How to close safely using Scalaz IO

  • Download Gist
IOTesting.scala
Scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
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)
}
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.