I am trying to implement the reader monad for the first time.
I want to use a monadic style to query a database.
usage example 1: the user has a one-to-one relationship with a colleague. Pseudo codegetUserById(getUserById(id).getColleague())
use case 2: get the list of users by id. Pseudo codeList(getUserById(id1), getUserById(id2))
These seem to be good use cases for monads. My goal is to see if I can use monads to improve my code.
PS: Please provide at least one answer without a scalp.
Here is the code:
package monad
import com.mongodb.casbah.Imports._
object Monad {
type UserId = Int
case class User(id: UserId, name: String, colleagueId: UserId)
trait Reader[I, A] { self =>
def run(id: I) : A
def map[B](f: A => B) : Reader[I, B] =
new Reader[I, B] { def run(id: I) = f(self.run(id)) }
def flatMap[B](f: A => Reader[I, B]) : Reader[I, B] =
new Reader[I, B] { def run(id: I) = f(self.run(id)).run(id) }
}
def coll = MongoClient()("test")("user")
def DBObject2User(o: DBObject) : User = User(o.as[Double]("id").toInt, o.as[String]("name"), o.as[Double]("colleague").toInt)
// Strange design, id is not used…
def User2Colleague(u: User) : Reader[UserId, DBObject] =
unit(coll.findOne(MongoDBObject("id" -> u.colleagueId)).get)
def GetUserById : Reader[UserId, DBObject] =
Reader { id: UserId => coll.findOne(MongoDBObject("id" -> id)).get }
def GetUserById2 : Reader[UserId, User] = GetUserById.map(DBObject2User)
def unit[A](a: => A) = Reader { id: UserId => a }
object Reader {
def apply[I, A](f: I => A) = new Reader[I, A] { def run(i: I) = f(i) }
}
def main(args: Array[String]) {
// I can do
println(GetUserById2.run(1))
// Same with for comprehension
val userReader = for (io <- GetUserById2) yield io
println(userReader.run(1))
//Combination to explore one-to-one relation
val user = GetUserById2.run(1)
val colleague = GetUserById2.run(user.colleagueId)
// Same with flatMap
println(GetUserById2.flatMap(User2Colleague).run(1))
// Same with for-comprehension but doesn't work
val io = for {io <- GetUserById2
io2 <- User2Colleague(io).map(DBObject2User)} yield io2
println(io.run(1))
//TODO: List[Reader] to Reader[List]
}
}
Is this a good way? I have some doubts, cf my commentStrange design
How can I improve my code?
source
share