3

我正在构建具有惊人数量抽象的 ubercool 应用程序。现在我要设计过度设计的EntityGetService。

我想要的第一件事是很多抽象类型,让我们这样说:

trait EntityGetService[ID, ENTITY, CONTAINER] {
  def get(id: ID): CONTAINER
}

这里的 CONTAINER 是包含(或不包含)请求实体的类型。与 Option[ENTITY] 非常相似。但我想要的第二件事是,CONTAINER 也可以是 Future[ENTITY]。

所以我真的想写这样的特质:

trait EntityGetService[ID, ENTITY, CONTAINER <: Wrapper[ENTITY]] {
    def get(id: ID): CONTAINER
}

并以下列方式指定它:

trait EntitySyncGetService[Long, Entity, Option[Entity]] {
    def get(id: Long): Option[Entity]
}

trait EntityAsyncGetService[Long, Entity, Future[Entity]] {
    def get(id: Long): Future[Entity]
}

有没有办法在不重新扩展或混合选项和未来的情况下做到这一点?

看起来 Option 和 Future 有一些共同点(它们都是容器)。它与单子有关吗?

或者这只是我失眠的产物?

4

2 回答 2

3

不评论这一切的合理性,您可以使用高阶类型:

trait WrappedGetService[ID, ENTITY, WRAPPER[_]] {
  def get(id: ID) : WRAPPER[ENTITY]
}

然后你可以声明一个WrappedGetService[Long, Entity, Option].

更新:一些参考

该功能称为高阶类型(也可能是高阶类型)或类型构造函数

  • 语言规范中,它应该主要出现在第 4.4 节,类型参数中,但不会有太多关于它的内容,它的工作原理与其他类型参数很相似。
  • 该功能最初是在论文Generics of a Higher kind中提出的,该论文可能无法完全按照语言实现,但应该非常接近。另一方面,收藏库则另辟蹊径,您可能会在使用 Types 对抗 Bit Rot中看到原因。
  • 你可以看看scalaz库(不适合胆小的人)。如果你这样做,你可能也想看看 Haskell。
于 2013-04-01T10:31:16.183 回答
3

您可以通过一些我称之为“手动 AOP”的方法来实现这一点。

首先定义一些基本特征来捕获将结果包装在 an Option、 aFuture或任何其他容器中的概念,以及实际执行包装的 aa 方法:

import concurrent._

trait AbstractService {
  type Result[T]
  protected def result[T]( code: => T ) : Result[T]
}

然后将这个基本特征专门用于FutureOption情况:

trait SyncService extends AbstractService {
  type Result[T] = Option[T]
  // Returns the original result wrapped in Some, 
  // or None if it was null or an exception occured
  protected def result[T]( code: => T ) : Option[T] = {
    try {
      Option( code )
    } catch{ case ex: Throwable =>
      ex.printStackTrace()
      None
    }
  }  
}

trait AsyncService extends AbstractService {
  type Result[T] = Future[T]
  protected val execContext: ExecutionContext
  protected def result[T]( code: => T ) : Future[T] = {
    future( code )( execContext )
  }
}

现在您可以开始了,可以像这样定义您的 EntityGetService 特征:

trait EntityGetService[ID, ENTITY] extends AbstractService {
  def get( id: ID ): Result[ENTITY] = result {
    ???
  }

  // add all the other method implementations here, taking care to 
  // wrap the method bodies inside "result"
}

// Now specializing EntityGetService as a sync or async service is just a matter 
// of mixing SyncService or AsyncService:

class EntitySyncGetService[Long, Entity] extends EntityGetService[Long, Entity] with SyncService
class EntityAsyncGetService[Long, Entity]( implicit val execContext: ExecutionContext ) extends EntityGetService[Long, Entity] with AsyncService
于 2013-04-01T11:03:54.540 回答