The most important reason is that the reader monad allows you to build up complex computations compositionally. Consider the following line from your non-reader example:
val rs1 = query("SELECT COUNT(*) FROM Foo")(conn)
The fact that we're passing in conn
manually means that this line doesn't really make sense on its own—it can only be understood and reasoned about in the context of the doSomeQueries
method that gives us conn
.
Often this is just fine—there's obviously nothing wrong about defining and using local variables (at least in the val
sense). Sometimes, though, it's more convenient (or desirable for other reasons) to build up computations out of stand-alone, composable pieces, and the reader monad can help with this.
Consider query("SELECT COUNT(*) FROM Foo")
in your second example. Assuming we know what query
is, this is an entirely self-contained expression—there are no variables like conn
that need to be bound by some enclosing scope. This means you can reuse and refactor more confidently, and that you don't have quite so much stuff to hold in your head when you're reasoning about it.
Again, this isn't ever necessary—it's largely a matter of style. If you decide to give it a try (and I'd suggest that you do), you'll probably pretty quickly develop preferences and intuitions about where it makes your code more intelligible and where it doesn't.
One other advantage is that you can compose different kinds of "effects" using ReaderT
(or by adding Reader
into some other stack). That set of issues probably deserves its own question and answer, though.
One last note: you probably want your doSomeQueries
to look like this:
def doSomeQueries: Reader[Connection, Int] = for {
rs1 <- query("SELECT COUNT(*) FROM Foo")
rs2 <- query("SELECT COUNT(*) FROM Bar")
} yield rs1.getInt(1) + rs2.getInt(1)
Or, if this really is the end of the line:
def doSomeQueries(conn: Connection) = (
for {
rs1 <- query("SELECT COUNT(*) FROM Foo")
rs2 <- query("SELECT COUNT(*) FROM Bar")
} yield rs1.getInt(1) + rs2.getInt(1)
).run(conn)
In your current version you're not actually using conn
.