Scala 更高级的类型 : can't define a generic function that works on different collections

标签 scala generics higher-kinded-types

我使用的是 Scala 2.10.2。

我需要一个函数

def extractEither(m: M[(Key, Either[TLeft, TRight])])
: Either[TLeft, M[(Key, TRight)]]

其中 M 可以是 Seq、List、Map 或其他任何值,并且返回类型仍然合适。

我使用以下进行测试:

val map = Map(1 -> Left("foo"), 2 -> Right('bar), 3 -> Right('baz))

我目前的尝试如下:

尝试#1

def extractEither[
  Key, TLeft, TRight, M[_] <: TraversableOnce[_]
]
(monad: M[(Key, Either[TLeft, TRight])])
(implicit cbf: CanBuildFrom[
M[(Key, Either[TLeft, TRight])],
  (Key, TRight),
  M[(Key, TRight)]
]): Either[TLeft, M[(Key, TRight)]] = {
  val builder = cbf(monad)
  builder.sizeHint(monad.size)
  (monad: GenTraversableOnce[_]).foreach { x =>
    val (key, either) = x.asInstanceOf[(Key, Either[TLeft, TRight])]
    either.fold(
      leftVal => return Left(leftVal),
      rightVal => builder += ((key, rightVal))
    )
  }
  Right(builder.result())
}

此操作失败:

scala> extractEither(map)
<console>:20: error: no type parameters for method extractEither: (monad: M[(Key, Either[TLeft,TRight])])(implicit cbf: scala.collection.generic.CanBuildFrom[M[(Key, Either[TLeft,TRight])],(Key, TRight),M[(Key, TRight)]])Either[TLeft,M[(Key, TRight)]] exist so that it can be applied to arguments (scala.collection.immutable.Map[Int,Product with Serializable with scala.util.Either[String,Symbol]])
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : scala.collection.immutable.Map[Int,Product with Serializable with scala.util.Either[String,Symbol]]
 required: ?M
              extractEither(map)
              ^
<console>:20: error: type mismatch;
 found   : scala.collection.immutable.Map[Int,Product with Serializable with scala.util.Either[String,Symbol]]
 required: M[(Key, Either[TLeft,TRight])]
              extractEither(map)
                            ^
<console>:20: error: Cannot construct a collection of type M[(Key, TRight)] with elements of type (Key, TRight) based on a collection of type M[(Key, Either[TLeft,TRight])].
              extractEither(map)
                           ^

尝试#2

这仅限于 map ,可变或不可变。

def extractEither[
  Key, TLeft, TRight, M <: collection.Map[Key, Either[TLeft, TRight]]
](map: M): Either[TLeft, M] = {
  Right[TLeft, M](map.map { case (key, either) =>
    either.fold(
      leftVal => return Left(leftVal),
      rightVal => key -> rightVal
    )
  }.asInstanceOf[M])
}

此操作失败:

scala> extractEither(map)
<console>:20: error: inferred type arguments [Nothing,Nothing,Nothing,scala.collection.immutable.Map[Int,Product with Serializable with scala.util.Either[String,Symbol]]] do not conform to method extractEither's type parameter bounds [Key,TLeft,TRight,M <: scala.collection.Map[Key,Either[TLeft,TRight]]]
              extractEither(map)
              ^
<console>:20: error: type mismatch;
 found   : scala.collection.immutable.Map[Int,Product with Serializable with scala.util.Either[String,Symbol]]
 required: M
              extractEither(map)
                            ^

有效的非通用解决方案

  def extractEither[
    Key, TLeft, TRight
  ](map: Map[Key, Either[TLeft, TRight]]): Either[TLeft, Map[Key, TRight]] = {
    Right(map.map { case (key, either) =>
      either.fold(
        leftVal => return Left(leftVal),
        rightVal => key -> rightVal
      )
    })
  }

但这根本不通用:|

任何人都可以阐明如何正确编写此内容吗?

最佳答案

第一个解决方案几乎已经存在,但您应该使用 M[X] <: TraversableOnce[X] 以避免强制转换。并且您需要向编译器输入提示,以获得正确的映射类型,无论是在将其传递给方法还是在定义它时。

scala> def extractEither[K, L, R, M[X] <: TraversableOnce[X]](monad: M[(K, Either[L, R])])(implicit cbf: CanBuildFrom[M[(K, Either[L, R])], (K, R), M[(K, R)]]): Either[L, M[(K, R)]] = {
 val builder = cbf(monad)
 builder.sizeHint(monad.size)
 monad.foreach({x =>
 val (key, either) = x
 either.fold(
 leftVal => return Left(leftVal),
 rightVal => builder += ((key, rightVal))
 )})
 Right(builder.result())
 }
warning: there were 1 feature warning(s); re-run with -feature for details
extractEither: [K, L, R, M[X] <: TraversableOnce[X]](monad: M[(K, Either[L,R])])(implicit cbf: scala.collection.generic.CanBuildFrom[M[(K, Either[L,R])],(K, R),M[(K, R)]])Either[L,M[(K, R)]]

scala> val map = Map(1 -> Left("foo"), 2 -> Right('bar), 3 -> Right('baz))
map: scala.collection.immutable.Map[Int,Product with Serializable with scala.util.Either[String,Symbol]] = Map(1 -> Left(foo), 2 -> Right('bar), 3 -> Right('baz))

scala> extractEither(map: TraversableOnce[(Int, Either[String, Symbol])])
res2: Either[String,TraversableOnce[(Int, Symbol)]] = Left(foo)

scala> val map2: Map[Int, Either[String, Symbol]] = Map(1 -> Left("foo"), 2 -> Right('bar), 3 -> Right('baz))
map2: Map[Int,Either[String,Symbol]] = Map(1 -> Left(foo), 2 -> Right('bar), 3 -> Right('baz))

scala> extractEither(map2)
res3: Either[String,scala.collection.immutable.Iterable[(Int, Symbol)]] = Left(foo)

关于Scala 更高级的类型 : can't define a generic function that works on different collections,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19159007/

相关文章:

c#获取泛型类中泛型类型参数的名称

scala - 一元类型构造函数的类型类模式

scala - 在 Akka Typed persistent actor 中创建子 actor

scala - 在 Play 中,如何在反向路由 URL 中包含自定义查询参数? (称呼)

scala - 远程 Spark 连接 - Scala : Could not find BlockManagerMaster

scala - 无法更新项目 '120' : HTTP 403 Forbidden 的 Gitlab 提交状态

java - 类型转换为泛型

delphi - 我可以使用泛型对类似类型的控件执行相同的操作吗?

scala - 使用成员访问而不是提取器时奇怪的类型不匹配

更高种类的 Scalacheck 问题 : diverging implicit expansion for type Arbitrary