“Deferred inline method `foo` in trait `Foo` cannot be invoked”: Pairs

huangapple go评论85阅读模式
英文:

"Deferred inline method `foo` in trait `Foo` cannot be invoked": Pairs

问题

以下是您提供的内容的中文翻译:

我只是在尝试Scala 3.2.2中giveninline的行为,遇到了以下示例:

  1. trait Max[X]:
  2. inline def max(a: X, b: X)
  3. inline given maxForDoubles: Max[Double] with
  4. inline def max(a: Double, b: Double) = if a < b then b else a
  5. inline given maxForPairs[A, B](using mA: Max[A], mB: Max[B]): Max[(A, B)] with
  6. inline def max(x: (A, B), y: (A, B)) =
  7. (mA.max(x._1, y._1), mB.max(x._2, y._2))
  8. @main def entryPoint(): Unit = {
  9. println(summon[Max[(Double, Double)]].max((10.0, 3.0), (20.0, -7.0)))
  10. }

不仅仅是打印出对偶(20, 3),它导致以下错误编译失败:

  1. 12 | println(summon[Max[(Double, Double)]].max((10.0, 3.0), (20.0, -7.0)))
  2. | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  3. | 无法调用Trait Max中的延迟内联方法max
  4. |----------------------------------------------------------------------------
  5. |内联堆栈跟踪
  6. |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  7. |此位置包含从buggy-example.scala:9内联的代码
  8. 9 | (mA.max(x._1, y._1), mB.max(x._2, y._2))
  9. | ^^^^^^
  10. ----------------------------------------------------------------------------
  11. 看起来应该在静态情况下知道一切为什么会失败呢
  12. <details>
  13. <summary>英文:</summary>
  14. I was just experimenting with the behavior of `given`s and `inline` in Scala 3.2.2, and ran into the following example:

trait Max[X]:
inline def max(a: X, b: X): X

inline given maxForDoubles: Max[Double] with
inline def max(a: Double, b: Double) = if a < b then b else a

inline given maxForPairs[A, B](using mA: Max[A], mB: Max[B]): Max[(A, B)] with
inline def max(x: (A, B), y: (A, B)) =
(mA.max(x._1, y._1), mB.max(x._2, y._2))

@main def entryPoint(): Unit = {
println(summon[Max[(Double, Double)]].max((10.0, 3.0), (20.0, -7.0)))
}

  1. Instead of just printing the pair `(20, 3)`, it fails the compilation with the following error:

12 | println(summon[Max[(Double, Double)]].max((10.0, 3.0), (20.0, -7.0)))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Deferred inline method max in trait Max cannot be invoked
Inline stack trace
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
This location contains code that was inlined from buggy-example.scala:9
9
^^^^^^
----------------------------------------------------------------------------
  1. It seems that it should know everything statically. Any clues why it&#39;s failing?
  2. </details>
  3. # 答案1
  4. **得分**: 4
  5. I'll provide a translation of the code snippets:
  6. ```scala
  7. 如果你尝试使用[别名 givens](https://docs.scala-lang.org/scala3/reference/contextual/givens.html#alias-givens)而不是[given 实例](https://docs.scala-lang.org/scala3/reference/contextual/givens.html#given-instances)(`with`-语法)
  8. ```scala
  9. inline given maxForDoubles: Max[Double] = new Max[Double]:
  10. inline override def max(a: Double, b: Double): Double = if a &lt; b then b else a
  11. inline given maxForPairs[A, B](using mA: Max[A], mB: Max[B]): Max[(A, B)] = new Max[(A, B)]:
  12. inline override def max(x: (A, B), y: (A, B)): (A, B) = (mA.max(x._1, y._1), mB.max(x._2, y._2))

这将无法工作,因为

  1. 实现限制不支持嵌套内联方法

但是使用 with-语法,嵌套的内联是允许的,因为

  1. inline given maxForDoubles: Max[Double] with
  2. inline override def max(a: Double, b: Double): Double = if a &lt; b then b else a
  3. inline given maxForPairs[A, B](using mA: Max[A], mB: Max[B]): Max[(A, B)] with
  4. inline override def max(x: (A, B), y: (A, B)): (A, B) = (mA.max(x._1, y._1), mB.max(x._2, y._2))

实际上相当于

  1. class maxForDoubles extends Max[Double]:
  2. inline override def max(a: Double, b: Double): Double = if a &lt; b then b else a
  3. inline given maxForDoubles: maxForDoubles = new maxForDoubles
  4. class maxForPairs[A, B](using mA: Max[A], mB: Max[B]) extends Max[(A, B)]:
  5. inline override def max(x: (A, B), y: (A, B)): (A, B) = (mA.max(x._1, y._1), mB.max(x._2, y._2))
  6. inline given maxForPairs[A, B](using mA: Max[A], mB: Max[B]): maxForPairs[A, B] = new maxForPairs[A, B]

实际上,使用 -Xprint:pickleQuotes 选项(或 -Xprint:typerwith-语法会产生

  1. // given class maxForDoubles() extends Object(), App.Max[Double] {
  2. // override inline def max(a: Double, b: Double): Double =
  3. // (if a.&lt;(b) then b else a):Double
  4. // }
  5. // final inline given def maxForDoubles: App.maxForDoubles =
  6. // new App.maxForDoubles():App.maxForDoubles
  7. // given class maxForPairs[A &gt;: Nothing &lt;: Any, B &gt;: Nothing &lt;: Any](using
  8. // mA: App.Max[A]
  9. // , mB: App.Max[B]) extends Object(), App.Max[
  10. // Tuple2[maxForPairs.this.A, maxForPairs.this.B]
  11. // ] {
  12. // A
  13. // B
  14. // protected given val mA: App.Max[A]
  15. // protected given val mB: App.Max[B]
  16. // override inline def max(x: Tuple2[maxForPairs.this.A, maxForPairs.this.B]
  17. // ,
  18. // y: Tuple2[maxForPairs.this.A, maxForPairs.this.B]): (A, B) =
  19. // Tuple2.apply[A, B](this.App$maxForPairs$$inline$mA.max(x._1, y._1),
  20. // this.App$maxForPairs$$inline$mB.max(x._2, y._2)
  21. // ):(A, B)
  22. // def App$maxForPairs$$inline$mA: App.Max[A] = maxForPairs.this.mA
  23. // def App$maxForPairs$$inline$mB: App.Max[B] = maxForPairs.this.mB
  24. // }
  25. // final inline given def maxForPairs[A &gt;: Nothing &lt;: Any, B &gt;: Nothing &lt;: Any]
  26. // (
  27. // using mA: App.Max[A], mB: App.Max[B]): App.maxForPairs[A, B] =
  28. // new App.maxForPairs[A, B](using mA, mB)():App.maxForPairs[A, B]

让我们暂时简化 with-语法,去掉 usingmaxForDoubles

  1. class maxForPairs[A, B]/*(using mA: Max[A], mB: Max[B])*/ extends Max[(A, B)]:
  2. inline override def max(x: (A, B), y: (A, B)): (A, B) = ??? // (mA.max(x._1, y._1), mB.max(x._2, y._2))
  3. // inline given maxForPairs[A, B]/*(using mA: Max[A], mB: Max[B])*/: maxForPairs[A, B] = new maxForPairs[A, B]

然后在

  1. val inst: maxForPairs[Double, Double] = new maxForPairs[Double, Double]
  2. inst.max((10.0, 3.0), (20.0, -7.0)) // 编译通过

  1. val inst: Max[(Double, Double)] = new maxForPairs[Double, Double]
  2. inst.max((10.0, 3.0), (20.0, -7.0)) // 编译失败:在特质 Max 中无法调用推迟的内联方法 max

之间的差异是可以理解的,因为有一个规则

> 3) 内联方法也可以是抽象的。抽象内联方法只能由其他内联方法实现。它不能直接调用:
>
> abstract class A:
> inline def f: Int
>
> object B extends A:
> inline def f: Int = 22
>
> B.f // OK
> val a: A = B
> a.f // error: 无法在 A 中内联 f。

https://docs.scala-lang.org/scala3/reference/metaprogramming/inline.html#rules-for-overriding

max 恰恰是特质 Max 中的一个抽象内联方法。

`sum

英文:

If you tried to use alias givens rather than given instances (with-syntax)

  1. inline given maxForDoubles: Max[Double] = new Max[Double]:
  2. inline override def max(a: Double, b: Double): Double = if a &lt; b then b else a
  3. inline given maxForPairs[A, B](using mA: Max[A], mB: Max[B]): Max[(A, B)] = new Max[(A, B)]:
  4. inline override def max(x: (A, B), y: (A, B)): (A, B) = (mA.max(x._1, y._1), mB.max(x._2, y._2))

this wouldn't work because of

  1. Implementation restriction: nested inline methods are not supported

But with with-syntax nested inline are allowed because

  1. inline given maxForDoubles: Max[Double] with
  2. inline override def max(a: Double, b: Double): Double = if a &lt; b then b else a
  3. inline given maxForPairs[A, B](using mA: Max[A], mB: Max[B]): Max[(A, B)] with
  4. inline override def max(x: (A, B), y: (A, B)): (A, B) = (mA.max(x._1, y._1), mB.max(x._2, y._2))

is actually

  1. class maxForDoubles extends Max[Double]:
  2. inline override def max(a: Double, b: Double): Double = if a &lt; b then b else a
  3. inline given maxForDoubles: maxForDoubles = new maxForDoubles
  4. class maxForPairs[A, B](using mA: Max[A], mB: Max[B]) extends Max[(A, B)]:
  5. inline override def max(x: (A, B), y: (A, B)): (A, B) = (mA.max(x._1, y._1), mB.max(x._2, y._2))
  6. inline given maxForPairs[A, B](using mA: Max[A], mB: Max[B]): maxForPairs[A, B] = new maxForPairs[A, B]

Indeed, with -Xprint:pickleQuotes option (or -Xprint:typer) the with-syntax produces

  1. // given class maxForDoubles() extends Object(), App.Max[Double] {
  2. // override inline def max(a: Double, b: Double): Double =
  3. // (if a.&lt;(b) then b else a):Double
  4. // }
  5. // final inline given def maxForDoubles: App.maxForDoubles =
  6. // new App.maxForDoubles():App.maxForDoubles
  7. // given class maxForPairs[A &gt;: Nothing &lt;: Any, B &gt;: Nothing &lt;: Any](using
  8. // mA: App.Max[A]
  9. // , mB: App.Max[B]) extends Object(), App.Max[
  10. // Tuple2[maxForPairs.this.A, maxForPairs.this.B]
  11. // ] {
  12. // A
  13. // B
  14. // protected given val mA: App.Max[A]
  15. // protected given val mB: App.Max[B]
  16. // override inline def max(x: Tuple2[maxForPairs.this.A, maxForPairs.this.B]
  17. // ,
  18. // y: Tuple2[maxForPairs.this.A, maxForPairs.this.B]): (A, B) =
  19. // Tuple2.apply[A, B](this.App$maxForPairs$$inline$mA.max(x._1, y._1),
  20. // this.App$maxForPairs$$inline$mB.max(x._2, y._2)
  21. // ):(A, B)
  22. // def App$maxForPairs$$inline$mA: App.Max[A] = maxForPairs.this.mA
  23. // def App$maxForPairs$$inline$mB: App.Max[B] = maxForPairs.this.mB
  24. // }
  25. // final inline given def maxForPairs[A &gt;: Nothing &lt;: Any, B &gt;: Nothing &lt;: Any]
  26. // (
  27. // using mA: App.Max[A], mB: App.Max[B]): App.maxForPairs[A, B] =
  28. // new App.maxForPairs[A, B](using mA, mB)():App.maxForPairs[A, B]

Let's temporarily simplify the with-syntax removing using and maxForDoubles

  1. class maxForPairs[A, B]/*(using mA: Max[A], mB: Max[B])*/ extends Max[(A, B)]:
  2. inline override def max(x: (A, B), y: (A, B)): (A, B) = ??? // (mA.max(x._1, y._1), mB.max(x._2, y._2))
  3. // inline given maxForPairs[A, B]/*(using mA: Max[A], mB: Max[B])*/: maxForPairs[A, B] = new maxForPairs[A, B]

Then the difference between

  1. val inst: maxForPairs[Double, Double] = new maxForPairs[Double, Double]
  2. inst.max((10.0, 3.0), (20.0, -7.0)) // compiles

and

  1. val inst: Max[(Double, Double)] = new maxForPairs[Double, Double]
  2. inst.max((10.0, 3.0), (20.0, -7.0)) // doesn&#39;t compile: Deferred inline method max in trait Max cannot be invoked

is understandable because there is a rule

> 3) Inline methods can also be abstract. An abstract inline method can be implemented only by other inline methods. It cannot be invoked directly:
>
> abstract class A:
> inline def f: Int
>
> object B extends A:
> inline def f: Int = 22
>
> B.f // OK
> val a: A = B
> a.f // error: cannot inline f in A.

https://docs.scala-lang.org/scala3/reference/metaprogramming/inline.html#rules-for-overriding

And max is exactly an abstract inline method in the trait Max.

summon[Max[(Double, Double)]] returns the precise type of implicit: maxForPairs[Double, Double] &lt;: Max[(Double, Double)] (as shapeless.the in Scala 2), not just Max[(Double, Double)] (as implicitly in Scala 2).

But if we restore using and maxForDoubles then for some reason this confuses inlines: both options for val inst (: maxForPairs[Double, Double] and : Max[(Double, Double)]) produce Deferred inline method max in trait Max cannot be invoked.

This seems to be a bug or underspecified feature. Try to open a ticket at https://github.com/lampepfl/dotty/issues

By the way, sometimes with-syntax behaves weirdly in comparison with alias givens (even without inlines):
https://github.com/lampepfl/dotty/issues/8882

huangapple
  • 本文由 发表于 2023年3月23日 10:26:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/75818787.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定