加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

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/(我不是作者,但这是一个明确的解释)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读