【发布时间】:2016-12-05 12:14:50
【问题描述】:
我经常发现自己在这样的场景中定义了一个接口:
trait FooInterface [T[_]] {
def barA (): T[Int]
def barB (): T[Int]
def barC (): T[Int]
}
然后,我编写了几个不同的实现,每个实现都在对特定实现最有意义的 Higher Kinded Type 上键入:
object FooImpl1 extends FooInterface[Option] { ... }
object FooImpl2 extends FooInterface[Future] { ... }
object FooImpl3 extends FooInterface[({type X[Y] = ReaderT[Future, Database, Y]})#X] { ... }
所有实现都是完全有效的,都返回包装在特定更高种类类型中的结果。
然后我经常来写一些业务逻辑,假设在我正在使用的逻辑块中使用Future作为上下文,我可能会写这样的东西:
val foo: FooInterface[Future] = ???
def fn (): Future[Int] = Future { 42 }
val result: Future[Int] = for {
x <- foo.barA ()
y <- foo.barB ()
z <- foo.barC ()
w <- fn ()
} yield x + y + z + w
上面的代码可以很好地与FooImpl2 一起工作,但是其他实现不能直接插入。在这种情况下,我总是写简单的适配器:
object FooImpl1Adapter extends FooInterface[Future] {
val t = new Exception ("Foo impl 1 failed.")
def barA (): Future[Int] = FooImpl1.barA () match {
case Some (num) => Future.successful (num)
case None => Future.failed (t)
}
def barB (): Future[Int] = FooImpl1.barB () match {
case Some (num) => Future.successful (num)
case None => Future.failed (t)
}
def barC (): Future[Int] = FooImpl1.barC () match {
case Some (num) => Future.successful (num)
case None => Future.failed (t)
}
}
case class FooImpl3Adapter (db: Database) extends FooInterface[Future] {
def barA (): Future[Int] = FooImpl3.barA ().run (db)
def barB (): Future[Int] = FooImpl3.barB ().run (db)
def barC (): Future[Int] = FooImpl3.barC ().run (db)
}
编写适配器很好,但它涉及大量样板,尤其是对于具有大量功能的接口;更重要的是,每种方法对每种方法都得到完全相同的适应处理。我真正想做的是lift 来自现有实现的适配器实现,只在适配机制中指定一次。
我想我希望能够写出这样的东西:
def generateAdapterFn[X[_], Y[_]] (implx: FooInterface[X])(f: X[?] => Y[?]): FooInterface[Y] = ???
所以我可以这样使用它:
val fooImpl1Adapter: FooInterface[Future] = generateAdapterFn [?, Future] () { z => z match {
case Some (obj) => Future.successful (obj)
case None => Future.failed (t)
}}
问题是:如何编写generateAdapterFn 函数?
我不确定如何解决这个问题,或者我的问题是否有其他常见模式或解决方案。我怀疑要编写我想要的generateAdapterFn 函数,我需要编写一个宏?如果可以,怎么做?
【问题讨论】:
标签: scala scalaz scala-macros scala-cats