【问题标题】:Why does this flatMap fail?为什么这个 flatMap 会失败?
【发布时间】:2013-03-12 06:30:08
【问题描述】:
scala> val s: Seq[Class[_ <: java.lang.Enum[_]]] = Seq(classOf[java.util.concurrent.TimeUnit])
s: Seq[Class[_ <: java.lang.Enum[_]]] = List(class java.util.concurrent.TimeUnit)

scala> s.flatMap(_.getEnumConstants)
<console>:9: error: no type parameters for method flatMap: (f: Class[_ <: java.lang.Enum[_]] => scala.collection.GenTraversableOnce[B])(implicit bf: scala.collection.generic.CanBuildFrom[Seq[Class[_ <: java.lang.Enum[_]]],B,That])That exist so that it can be applied to arguments (Class[_ <: java.lang.Enum[_]] => scala.collection.mutable.ArrayOps[_$1(in value $anonfun) with java.lang.Object] forSome { type _$1(in value $anonfun) <: java.lang.Enum[_] })
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : Class[_ <: java.lang.Enum[_]] => scala.collection.mutable.ArrayOps[_$1(in value $anonfun) with java.lang.Object] forSome { type _$1(in value $anonfun) <: java.lang.Enum[_] }
 required: Class[_ <: java.lang.Enum[_]] => scala.collection.GenTr...              s.flatMap(_.getEnumConstants)

【问题讨论】:

    标签: scala


    【解决方案1】:

    不是真正的准确答案,但有两个观察结果 - Scala 2.10 会给你一个更好的错误:

    scala> s.flatMap(_.getEnumConstants)
    <console>:9: error: no type parameters for method flatMap: (f: Class[_ <: Enum[_]] => scala.collection.GenTraversableOnce[B])(implicit bf: scala.collection.generic.CanBuildFrom[Seq[Class[_ <: Enum[_]]],B,That])That exist so that it can be applied to arguments (Class[_ <: Enum[_]] => scala.collection.mutable.ArrayOps[(some other)_$1(in object $iw) with Object] forSome { type (some other)_$1(in object $iw) <: Enum[_] })
     --- because ---
    argument expression's type is not compatible with formal parameter type;
     found   : Class[_ <: Enum[_]] => scala.collection.mutable.ArrayOps[(some other)_$1(in object $iw) with Object] forSome { type (some other)_$1(in object $iw) <: Enum[_] }
     required: Class[_ <: Enum[_]] => scala.collection.GenTraversableOnce[?B]
                  s.flatMap(_.getEnumConstants)
                    ^
    <console>:9: error: type mismatch;
     found   : Class[_ <: Enum[_]] => scala.collection.mutable.ArrayOps[(some other)_$1(in object $iw) with Object] forSome { type (some other)_$1(in object $iw) <: Enum[_] }
     required: Class[_ <: Enum[_]] => scala.collection.GenTraversableOnce[B]
                  s.flatMap(_.getEnumConstants)
                              ^
    <console>:9: error: Cannot construct a collection of type That with elements of type B based on a collection of type Seq[Class[_ <: Enum[_]]].
                  s.flatMap(_.getEnumConstants)
                       ^
    

    而且,如果您拆分 flatMap,您会看到问题的更简单版本:

    scala> s.map(_.getEnumConstants)
    res28: Seq[Array[_$1 with Object] forSome { type _$1 <: Enum[_] }] = List(Array(NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS), Array(RELEASE_0, RELEASE_1, RELEASE_2, RELEASE_3, RELEASE_4, RELEASE_5, RELEASE_6))
    scala> res28.flatten
    <console>:10: error: No implicit view available from Array[_$1 with Object] forSome { type _$1 <: Enum[_] } => scala.collection.GenTraversableOnce[B].
                  res28.flatten
                        ^
    

    这相当令人惊讶,因为您认为将Array 转换为GenTraversableOnce 应该很容易。我目前没有时间挖掘细节,但我会指出以下事情似乎有效:

    s.flatMap(_.getEnumConstants.toSeq)
    s.flatMap(_.getEnumConstants.map(_.asInstanceOf[Enum[_]]))
    

    我投票给编译器错误,因为this gist,它在这个简单脚本的 REPL 中显示了一些非常奇怪的行为

    val s: Seq[Class[_ <: java.lang.Enum[_]]] = Seq(classOf[java.util.concurrent.TimeUnit], classOf[javax.lang.model.SourceVersion])
    s.flatMap(_.getEnumConstants.toSeq)
    s.flatMap(_.getEnumConstants.toArray)
    1234
    

    【讨论】:

    • 正如我在其他评论中指出的那样,我希望Predef.genericArrayOps 提供从Array[TimeUnit]ArrayOps[TimeUnit] 的所需转换,它继承自GenTraversableOnce[TimeUnit],正如预期的flatMap打电话。
    • 我同意,问题归结为为什么它似乎没有触发隐式(也许它是否应该)。
    • 我现在的猜测是问题与所需的隐式 CanBuildFrom[Seq[Class[_ &lt;: java.lang.Enum[_]]], TimeUnit, Seq[TimeUnit]] 有关,但目前我找不到哪个源文件包含默认的隐式类型类构建器。我想发帖到 scala-users 组会征求一些更明智的答案。
    【解决方案2】:

    TimeUnit.getEnumConstants 返回一个 java 数组 TimeUnit[],而 flatMap 需要一个 GenTraversable

    你可以过得去

    scala> s.flatMap(_.getEnumConstants.toSeq)
    res4: Seq[Enum[_]] = List(NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS)
    

    【讨论】:

    • 我一头雾水,我的简单理解是Java TimeUnit[] 和Scala 中的Array[TimeUnit] 是同一个类型,所以应该没有区别,但我可能错了。
    • 实际上我开始怀疑自己是否应该按预期工作。我应该查看一些低级规范或手册。
    • 我看到的是Array[T]没有直接继承Traversable-some trait,但它必须从Predef.genericArrayOpsPredef.genericWrapArray隐式转换才能得到traversable -like 操作。可能是隐式转换在作为参数传递给flatMap 的 lambda 上下文中不可用,尽管我不明白为什么。
    猜你喜欢
    • 1970-01-01
    • 2012-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-29
    • 2021-03-17
    • 1970-01-01
    相关资源
    最近更新 更多