【问题标题】:Scala: Generics For Return Type Seq[A] or Future[Seq[A]]Scala:返回类型 Seq[A] 或 Future[Seq[A]] 的泛型
【发布时间】:2014-01-13 22:36:48
【问题描述】:

问题

我有两个类如下所示:

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]?有没有办法让现在和以后共享同一个界面?

【问题讨论】:

    标签: scala generics higher-kinded-types


    【解决方案1】:

    你需要类型组合:

    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(我可以发誓它包括一般类型的组合,但显然不是。)

    【讨论】:

    • 谢谢。引用此类类型的方法是否有名称?我以前从未见过它,想了解更多。
    • @JakeGreene 它被称为 type lambda。看到这个问题:stackoverflow.com/questions/8736164/…
    【解决方案2】:

    相信你想要这个:

    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 的类型组合答案就是您要寻找的答案。

    【讨论】:

    • 'type Id[A] = A' 是一个不错的小技巧。我想知道scalaz是否已经有了这样的东西
    【解决方案3】:

    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,它基本上对它包含的对象来说就像一个惰性标识。同样还有其他替代方案,但重点是,关于FutureNeed,您唯一需要了解的是它们是单子。您对它们一视同仁,并决定在其他地方使用其中一种。

    【讨论】:

      猜你喜欢
      • 2019-09-23
      • 2020-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多