我第一次尝试实现一个 reader monad。
我想使用 monadic 样式来查询数据库。
用例 1:用户与同事具有一对一的关系。伪代码是getUserById(getUserById(id).getColleague())
用例 2:按 id 检索用户列表。伪代码是List(getUserById(id1), getUserById(id2))
这似乎是 monad 的好用例。我的目标是看看我是否可以利用 monads 来改进我的代码
PS:请提供至少一个没有scalaz的答案。
这是代码:
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]
}
}
这是好方法吗?我有一些疑问,请参阅我的评论Strange design
我怎样才能改进我的代码?