【问题标题】:Scala: missing parameter typeScala:缺少参数类型
【发布时间】:2015-01-27 15:03:32
【问题描述】:

我在 Scala-REPL 中输入了以下内容:

scala> List(1, 2, 3).toSet.subsets(2).map(_.toList)
res0: Iterator[List[Int]] = non-empty iterator

scala> List(1, 2, 3).toSet.subsets.map(_.toList)
<console>:8: error: missing parameter type for expanded function ((x$1) => x$1.toList)
              List(1, 2, 3).toSet.subsets.map(_.toList)

为什么第二行出现错误?这是编译器中的错误还是我遗漏了什么?

【问题讨论】:

  • 我猜.subsets 更加模棱两可(它可能是带有参数的版本的部分应用),因此类型推断效果不太好。在任何情况下,您都可以通过执行编译器告诉您的操作并提供显式类型来使其编译:List(1, 2, 3).toSet.subsets.map{x: Set[Int] =&gt; x.toList}
  • 但是这两个方法的返回类型签名相同,根据 2.10.4 scaladoc:def subsets: Iterator[Set[A]],def subsets(len: Int): Iterator[Set[A]] 所以就类型而言应该没有任何区别,对吧?
  • 在 Scala 中允许这些方法一起使用,而 def subsets(i: Int)(d: Double): Iterator[Set[A]] 方法会导致 ambiguous reference to overloaded definition 错误,这不是不一致吗?
  • 如果是Set(1, 2, 3).subsets,它可以工作。如果您将其与...subsets match { case x =&gt; x.map(_.toList) } 分开,它也可以工作。
  • @the21st 的不对称性在于subsets 可以是subsets(Int) 的部分应用,而subsets(2) 不能是subsets 的部分应用。 (但这只是一个猜测)。

标签: scala


【解决方案1】:

自相矛盾的是,第一个版本之所以有效,是因为应用程序 subsets(2) 中的 subsets 比没有括号时更模棱两可。

由于方法重载,在应用程序中,编译器暂停求解toSetB结果,并确定BInt。所以它知道map 的预期参数类型。

在没有括号的版本中,带有参数列表的方法不是候选方法,因为没有触发 eta-expansion。所以当它键入map应用程序时,它并没有得出任何关于B的结论,这是映射函数的输入类型。

简单的解决方法是告诉它推断B

trait Test {
  def f1 = List(1, 2, 3).to[Set].subsets.map(_.toList) // instead of .toSet
  def f2 = List(1, 2, 3).toSet.subsets(2).map(_.toList)
}

-Ytyper-debug 在原始代码上的输出显示了重载解析如何影响类型推断:

|    |    |    |    |    |    \-> => Iterator[scala.collection.immutable.Set[B]] <and> (len: Int)Iterator[scala.collection.immutable.Set[B]]
|    |    |    |    |    solving for (B: ?B)
|    |    |    |    |    |-- 2 : pt=Int BYVALmode-EXPRmode-POLYmode (silent: method f2 in Test) 
|    |    |    |    |    |    \-> Int(2)
|    |    |    |    |    solving for (B: ?B)
|    |    |    |    |    \-> Iterator[scala.collection.immutable.Set[Int]]

另一种解决方法是通过扩展方法:

scala> implicit class ss[A](val s: Set[A]) { def ss(n: Int) = s subsets n ; def ss = s.subsets }
defined class ss

scala> List(1, 2, 3).toSet.ss.map(_.toList)
res1: Iterator[List[Int]] = non-empty iterator

让我们看看他们是否会接受图书馆更改:

https://github.com/scala/scala/pull/4270

【讨论】:

    【解决方案2】:

    这很可能是编译器本身无法进行类型推断的问题。我之前也遇到过类似的问题。

    如果您快速浏览一下subsets()subsets(len: Int)Set 的定义,它们共享相同的返回类型,所以它们应该都可以工作,对吧?

    为了解决这个问题,这将起作用:

    (List(1, 2, 3).toSet.subsets: Iterator[Set[Int]]).map(_.toList)
    

    【讨论】:

    • 您错误地引用了subsets,它没有参数列表。否则不会有问题
    【解决方案3】:

    子集可以部分应用其他重载。

    【讨论】:

      【解决方案4】:

      这似乎是编译器问题。

      比较:

      scala> List(1,2,3).toSet.subsets
      res4: Iterator[scala.collection.immutable.Set[Int]] = non-empty iterator
      
      scala> res4.map(_.toList)
      res5: Iterator[List[Int]] = non-empty iterator
      

      【讨论】:

        猜你喜欢
        • 2020-08-04
        • 2012-02-23
        • 2017-06-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多