问题
我有两个类如下:
class Now {
def do[A](f: Int => A): Seq[A]
}
class Later {
def do[A](f: Int => A): Future[Seq[A]]
}
两个类之间的唯一区别是,Now返回Seq,而Later返回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]]
}
我是否错误地使用了更高级别的类型?我错误地提供了Future [Seq]吗?有没有办法让Now和Later共享同一个界面?
答案 0 :(得分:4)
您需要类型组合:
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(我可以宣誓它包括一般类型的构图,但显然不是。)
答案 1 :(得分:1)
我相信你想要这个:
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的类型组合答案就是你要找的那个。
答案 2 :(得分:1)
Alexey的解决方案非常聪明,可以回答你提出的问题。但是,我认为你提出了这个问题。
您从这两个界面开始:
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
的唯一事情是它们是monad。你对待它们是一样的,你决定在其他地方使用其中一个。