如何结合Kleisli [M,A,C]和Kleisli [M,B,C]

时间:2016-03-17 14:33:37

标签: scala functional-programming scalaz kleisli

我遵循优秀书籍反应域建模的设计,我需要将Kleisli与不同类型混合:

object CombinedKleisli {
  type User = String
  type Project = String

  trait UserRepo
  trait ProjectRepo
  trait UserService {
    def findByUserId : Kleisli[Future, UserRepo, User]
  }
  trait ProjectService {
    def findProjectById : Kleisli[Future, ProjectRepo, Project]
  }
  trait ComposedService extends UserService with ProjectService {
    for {
      user <- findByUserId
      project <- findProjectById
    } yield (user, project)
  }

}

由于类型不对齐,我得到以下编译错误

Error:(28, 15) type mismatch;
 found   : scalaz.Kleisli[scala.concurrent.Future,domain.service.ServiceTest.ProjectRepo,(domain.service.ServiceTest.User, domain.service.ServiceTest.Project)]
    (which expands to)  scalaz.Kleisli[scala.concurrent.Future,domain.service.ServiceTest.ProjectRepo,(String, String)]
 required: scalaz.Kleisli[scala.concurrent.Future,domain.service.ServiceTest.UserRepo,?]
      project <- findProjectById
              ^

解决这个问题的最佳方法是什么,创建一个

trait Context {
  def userRepo
  def projectRepo
}

并用它污染UserServiceProjectService

1 个答案:

答案 0 :(得分:4)

您需要想出一些方法将输入类型合并为一种类型。一种方法是继承 - 您的类型UserRepo with ProjectRepoUserRepoProjectRepo的子类。另一种方法是组合,你有一个元组(UserRepo, ProjectRepo)

在这两种情况下,您通常都会使用local来扩展&#34;每个箭头的输入类型,以便您可以在for - 理解:

中组合它们
for {
  user <- findByUserId.local[(UserRepo, ProjectRepo)](_._1)
  project <- findProjectById.local[(UserRepo, ProjectRepo)](_._2)
} yield (user, project)

此处(UserRepo, ProjectRepo)类型参数参数指定新输入类型,值参数(例如_._1)指定如何从新输入类型获取原始箭头的输入类型。