【问题标题】:Scala view application puzzlerScala 视图应用程序谜题
【发布时间】:2012-11-10 23:56:10
【问题描述】:

假设我们有以下两个特征:

trait Foo[A] { def howMany(xs: List[A]) = xs.size }
trait Bar

还有从第二个到第一个的隐式转换:

implicit def bar2foo[A](bar: Bar) = new Foo[A] {}

我们创建一个Bar 和一个整数列表:

val bar = new Bar {}
val stuff = List(1, 2, 3)

现在我希望以下工作:

bar howMany stuff

但它没有:

scala> bar howMany stuff
<console>:13: error: type mismatch;
 found   : List[Int]
 required: List[A]
              bar howMany stuff
                          ^

所以我们去the spec,它有这样的说法(粗体强调是我的):

视图应用于三种情况。

  1. [此处不相关。]

  2. 在选择 em 中,e 类型为 T如果选择器 m 不表示 T 的成员。在这种情况下,搜索一个视图 v 适用于 e 并且其结果包含名为 m 的成员。这 搜索与隐式参数的情况一样进行,其中 隐式作用域是 T 之一。如果找到这样的视图, 选择 e.m 被转换为 v(e).m

  3. 在选择 em(args) 中,e 类型为 T如果选择器 m 表示 T 的一些成员,但这些成员都不适用于参数 args。在这种情况下,搜索视图 v 适用于 e 并且其结果包含方法 m 这适用于 args。搜索继续进行 隐式参数,其中隐式范围是 T 之一。如果 找到这样的视图,选择 e.m 转换为 v(e).m(args).

所以我们尝试了以下方法,认为它一定太荒谬了:

trait Foo[A] { def howMany(xs: List[A]) = xs.size }
trait Bar { def howMany = throw new Exception("I don't do anything!") }

implicit def bar2foo[A](bar: Bar) = new Foo[A] {}

val bar = new Bar {}
val stuff = List(1, 2, 3)

但确实如此(至少在 2.9.2 和 2.10.0-RC2 上):

scala> bar howMany stuff
res0: Int = 3

这会导致一些非常奇怪的行为,例如this workaround 中的this problem

我有三个(密切相关的)问题:

  1. 是否有一种直接的方法(即不涉及添加具有适当名称的假方法)在上述原始案例中正确应用视图?
  2. 有人可以提供解释这种行为的规范吗?
  3. 假设这是预期的行为,这是否有意义?

我也非常感谢您之前讨论过这个问题的任何链接——我对 Google 的运气并不好。

【问题讨论】:

    标签: scala types implicits


    【解决方案1】:

    供大家参考,这可能只是一个错误。您知道的方式是错误消息:

    <console>:13: error: type mismatch;
     found   : List[Int]
     required: List[A]
    

    List[A] 不是真正的类型 - 它是 List 应用于它自己的类型参数。这不是一种可以被要求的类型,因为它不是一种可以表达的类型。

    [编辑——太早了,谁知道我在说什么。忽略以上,但您仍然可以点击链接。]

    与此相关的票是https://issues.scala-lang.org/browse/SI-6472

    【讨论】:

      【解决方案2】:

      这似乎是一个错误,所以我的答案是:

      1. 搜索针对 Scala 编译器报告的类似错误,如果未找到,请报告新错误 https://issues.scala-lang.org/
      2. 在这种情况下,规范的那部分似乎并不重要,因为它没有讨论类型推断
      3. 对我来说没有任何意义

      PS。在 2.8.1 中,将虚拟方法添加到 Bar 的解决方法不会使其编译。

      【讨论】:

        【解决方案3】:

        用这个替换你的 Foo:

        trait Foo[_] { def howMany(xs: List[_]) = xs.size }

        它有效,这对我来说也更有意义,因为你对 A 绝对不感兴趣。

        【讨论】:

        • 这适用于特定的简化情况,但总的来说,我确实关心A——例如,在问题中链接的 Scalaz 问题的解决方法中。
        【解决方案4】:

        您的隐式转换似乎完全按照您的指示进行。

        implicit def bar2foo[A](bar: Bar) = new Foo[A] {}
        

        将 Bar 转换为新的 Foo[A] 对象。所以反过来

        scala> bar howMany stuff
        <console>:13: error: type mismatch;
         found   : List[Int]
         required: List[A]
                      bar howMany stuff
        

        它寻找“A”类型。

        为了使这项工作按照您想要的方式进行(我认为),您可以在函数上执行此操作,而不是定义特征的视图。

        trait Foo { def howMany[A](xs: List[A]) = xs.size }
        trait Bar
        implicit def bar2foo[A](bar: Bar) = new Foo{}
        val bar = new Bar {}
        val stuff = List(1, 2, 3)
        

        那么它应该会给你想要的结果。

        scala> bar howMany stuff
        res0: Int = 3
        

        或者你可以在隐式函数上定义视图

        trait Foo[A] { def howMany(xs: List[A]) = xs.size }
        trait Bar
        
        implicit def bar2foo[A](bar: Bar) = new Foo[Int] {}
        
        val bar = new Bar {}
        val stuff = List(1, 2, 3)
        

        我个人认为在函数上定义它更干净。

        【讨论】:

        • 我想要的是“明显”的行为——即,根据howMany 的参数推断FooA 参数。这显然是可能的,因为我可以通过向Bar 添加(完全任意定义的)假howMany 方法来实现它。
        【解决方案5】:

        这虽然丑陋,但似乎有效:

        (bar: Foo[Int]) howMany stuff
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-11-17
          • 1970-01-01
          • 1970-01-01
          • 2010-12-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多