问题
我有两个类,如下所示:
class Now {
def do[A](f: Int => A): Seq[A]
}
class Later {
def do[A](f: Int => A): Future[Seq[A]]
}
这两个类之间的唯一区别是 Now 返回一个 Seq,稍后返回一个 Future Seq。我希望这两个类共享相同的接口
我试过什么
这似乎非常适合高级类型,考虑到 Seq 和 Future[Seq] 应该只需要一个类型参数。
trait Do[F[_]] {
def do[A](f: Int => A): F[A]
}
// Compiles
class Now extends Do[Seq] {
def do[A](f: Int => A): Seq[A]
}
// Does not compile. "type Seq takes type parameters" and
// "scala.concurrent.Future[<error>] takes no type parameters, expected: one"
class Later extends Do[Future[Seq]] {
def do[A](f: Int => A): Future[Seq[A]]
}
我是否错误地使用了更高种类的类型?我是否错误地供应了未来[序列]?有没有办法允许现在和以后共享同一个界面?
你需要类型组合:
trait Composition[F[_], G[_]] { type T[A] = F[G[A]] }
class Later extends Do[Composition[Future, Seq]#T] {
def do[A](f: Int => A): Future[Seq[A]]
}
或者如果你只需要它在一个地方
class Later extends Do[({ type T[A] = Future[Seq[A]] })#T] {
def do[A](f: Int => A): Future[Seq[A]]
}
参见 scalaz(我可以发誓它包括一般类型的组合,但显然不是。
我相信你想要这个:
import scala.language.higherKinds
import scala.concurrent.Future
object Main {
type Id[A] = A
trait Do[F[_]] {
// Notice the return type now contains `Seq`.
def `do`[A](f: Int => A): F[Seq[A]]
}
class Now extends Do[Id] {
override def `do`[A](f: Int => A): Seq[A] = ???
}
class Later extends Do[Future] {
override def `do`[A](f: Int => A): Future[Seq[A]] = ???
}
}
但是,如果你想要更通用的东西,其中抽象方法在其返回类型中是完全泛型的,那么@AlexeyRomanov的类型组合答案就是你要找的答案。
阿列克谢的解决方案非常聪明,回答了你提出的问题。但是,我认为您提出了问题。
您从以下两个接口开始:
class Now {
def do[A](f: Int => A): Seq[A]
}
class Later {
def do[A](f: Int => A): Future[Seq[A]]
}
并希望修改Later
以便实现以下内容:
trait Do[F[_]] {
def do[A](f: Int => A): F[A]
}
然而,你在这里失去了一个机会来抽象出某些东西是现在还是以后。相反,您应该将Do
更改为如下所示:
trait Do[F[_]] {
def do[A](f: Int => A): F[Seq[A]]
}
并将Now
更改为:
class Now {
def do[A](f: Int => A): Need[Seq[A]]
}
在这里,Need 是一个 Scalaz monad,它基本上就像它所包含的对象的懒惰身份。同样还有其他选择,但关键是,关于Future
和Need
,您唯一需要了解的是它们是单子。你对它们一视同仁,然后决定在其他地方使用其中一个。