scala – 如何实现reader monad访问数据库
发布时间:2020-12-16 09:50:12 所属栏目:安全 来源:网络整理
导读:我尝试第一次实现读者monad. 我想使用monadic样式来查询数据库. 用例1:用户与同事有一对一的关系.伪代码是getUserById(getUserById(id).getColleague()) 用例2:按ID检索用户列表.伪代码是List(getUserById(id1),getUserById(id2)) 这似乎是monad的好用例.
我尝试第一次实现读者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] } } 这是好方法吗?我有一些疑问,参见我的评论奇怪的设计 我怎么能改进我的代码? 解决方法
我试图重新设计你的提议,使收集成为读者的输入,在我去的时候重新命名.
package monad package object reader{ type UserId = Int } package reader { case class User(id: UserId,colleagueId: UserId) import com.mongodb.casbah.Imports._ import com.mongodb.casbah trait Reader[I,A] { self => val run = apply _ def apply(id:I):A def map[B](f: A => B): Reader[I,B] = new Reader[I,B] { def apply(id: I) = f(self.run(id)) } def flatMap[B](f: A => Reader[I,B]): Reader[I,B] { def apply(id: I) = f(self(id)).run(id) } } object Reader { def unit[A](a: => A) = apply { id: UserId => a } def apply[I,A] { def apply(i: I) = f(i) } } object Users { def asUser(o: DBObject): User = User(o.as[Double]("id").toInt,o.as[Double]("colleague").toInt) def colleague(u: User): Reader[MongoCollection,User] = Reader{ coll => asUser(coll.findOne(MongoDBObject("id" -> u.colleagueId)).get) } def getUserById(id:UserId): Reader[MongoCollection,User] = Reader { coll => asUser(coll.findOne(MongoDBObject("id" -> id)).get) } } object Client extends App { import Users._ def coll: casbah.MongoCollection = MongoClient()("test")("user") // I can do println(getUserById(1)(coll)) // Same with for comprehension val userReader = for (user <- getUserById(1)) yield user println(userReader(coll)) //Combination to explore one-to-one relation val user = getUserById(1)(coll) val otherUser = getUserById(user.colleagueId)(coll) // Same with flatMap println(getUserById(1).flatMap(colleague)(coll)) // Same with for-comprehension but doesn't work val coworkerReader = for {user <- getUserById(1) coworker <- colleague(user)} yield coworker println(coworkerReader(coll)) } } 使用这种方法我认为代码更容易测试,因为您可以传递依赖项(MongoCollection),同时只操作签名中的值和函数.阅读更多内容http://blog.originate.com/blog/2013/10/21/reader-monad-for-dependency-injection/(我不是作者,但这是一个明确的解释) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |