import java.io.Closeable
case class Closer[C <: Closeable, A](unsafeRun: C => A) {
def run(c: C): A = {
val a = unsafeRun(c)
println("***CLOSING***")
.close()
c
a}
}
object Closer {
implicit def closerMonad[C <: Closeable]: Monad[({ type λ[ɑ] = Closer[C, ɑ] })#λ] =
new Monad[({ type λ[ɑ] = Closer[C, ɑ] })#λ] {
def pure[A](a: A): Closer[C, A] =
Closer(_ => a)
def flatMap[A,B](ma: Closer[C, A])(f: A => Closer[C, B]): Closer[C, B] =
{ c: C =>
Closer val a: A = ma.unsafeRun(c)
f(a).unsafeRun(c)
}
}
implicit def function1[C <: Closeable, A](f: C => A): Closer[C, A] =
new Closer(f)
}
For the following examples, we'll use some common functions.
import java.io.RandomAccessFile
import Closer._
import Monad._
// opens a RandomAccessFile for reading
def file = {
println("***OPENING***")
new RandomAccessFile("jabberwocky.txt", "r")
}
// reads (if possible) a line from a RandomAccessFile, and sets up for reading
// the next line (if necessary)
val cat: RandomAccessFile => Stream[String] =
{ x =>
println("***READING LINE***")
Option(x.readLine()) match {
case None => Stream.empty
case Some(line) => Stream(line) #::: cat(x)
}
}
We can do this with a bunch of calls to map
...
val closer: Closer[RandomAccessFile, Stream[String]] = // requires Scala 2.13 (33478bd)
cat
val closer1 = closer map { _.headOption } map { _ foreach println }
closer1 run file
/* Output:
***OPENING***
***READING LINE***
'Twas brillig, and the slithy toves
***CLOSING***
*/
...or with a for
comprehension.
val closer2 =
for {
<- closer
stream = stream.headOption
lineO = lineO map println
_ } yield Unit
closer2 run file
/* Output:
***OPENING***
***READING LINE***
'Twas brillig, and the slithy toves
***CLOSING***
*/
This acts like the Unix head
command.
val closer3 =
for {
<- closer
stream = stream.take(4)
lines = lines foreach println
_ } yield Unit
closer3 run file
/* Output:
***OPENING***
***READING LINE***
'Twas brillig, and the slithy toves
***READING LINE***
Did gyre and gimble in the wabe;
***READING LINE***
All mimsy were the borogoves,
***READING LINE***
And the mome raths outgrabe.
***CLOSING***
*/
Here we lazily read the file and print each line as we go.
val closer4 = closer map { _ foreach println }
closer4 run file
/*
***OPENING***
***READING LINE***
'Twas brillig, and the slithy toves
***READING LINE***
Did gyre and gimble in the wabe;
***READING LINE***
All mimsy were the borogoves,
***READING LINE***
And the mome raths outgrabe.
***READING LINE***
***CLOSING***
*/
With this we can lazily read the file, print each line, then print each line again without re-reading the file.
val closer5 =
for {
<- closer
stream = stream foreach println
_ = stream foreach println
_ } yield Unit
closer5 run file
/*
***OPENING***
***READING LINE***
'Twas brillig, and the slithy toves
***READING LINE***
Did gyre and gimble in the wabe;
***READING LINE***
All mimsy were the borogoves,
***READING LINE***
And the mome raths outgrabe.
***READING LINE***
'Twas brillig, and the slithy toves
Did gyre and gimble in the wabe;
All mimsy were the borogoves,
And the mome raths outgrabe.
***CLOSING***
*/