英文:
"Deferred inline method `foo` in trait `Foo` cannot be invoked": Pairs
问题
以下是您提供的内容的中文翻译:
我只是在尝试Scala 3.2.2中given
和inline
的行为,遇到了以下示例:
trait Max[X]:
inline def max(a: X, b: 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)))
}
不仅仅是打印出对偶(20, 3)
,它导致以下错误编译失败:
12 | println(summon[Max[(Double, Double)]].max((10.0, 3.0), (20.0, -7.0)))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| 无法调用Trait Max中的延迟内联方法max
|----------------------------------------------------------------------------
|内联堆栈跟踪
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|此位置包含从buggy-example.scala:9内联的代码
9 | (mA.max(x._1, y._1), mB.max(x._2, y._2))
| ^^^^^^
----------------------------------------------------------------------------
看起来应该在静态情况下知道一切。为什么会失败呢?
<details>
<summary>英文:</summary>
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)))
}
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 |
^^^^^^ |
---------------------------------------------------------------------------- |
It seems that it should know everything statically. Any clues why it's failing?
</details>
# 答案1
**得分**: 4
I'll provide a translation of the code snippets:
```scala
如果你尝试使用[别名 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`-语法)
```scala
inline given maxForDoubles: Max[Double] = new Max[Double]:
inline override def max(a: Double, b: Double): Double = if a < b then b else a
inline given maxForPairs[A, B](using mA: Max[A], mB: Max[B]): Max[(A, B)] = new Max[(A, B)]:
inline override def max(x: (A, B), y: (A, B)): (A, B) = (mA.max(x._1, y._1), mB.max(x._2, y._2))
这将无法工作,因为
实现限制:不支持嵌套内联方法
但是使用 with
-语法,嵌套的内联是允许的,因为
inline given maxForDoubles: Max[Double] with
inline override def max(a: Double, b: Double): 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 override def max(x: (A, B), y: (A, B)): (A, B) = (mA.max(x._1, y._1), mB.max(x._2, y._2))
实际上相当于
class maxForDoubles extends Max[Double]:
inline override def max(a: Double, b: Double): Double = if a < b then b else a
inline given maxForDoubles: maxForDoubles = new maxForDoubles
class maxForPairs[A, B](using mA: Max[A], mB: Max[B]) extends Max[(A, B)]:
inline override def max(x: (A, B), y: (A, B)): (A, B) = (mA.max(x._1, y._1), mB.max(x._2, y._2))
inline given maxForPairs[A, B](using mA: Max[A], mB: Max[B]): maxForPairs[A, B] = new maxForPairs[A, B]
实际上,使用 -Xprint:pickleQuotes
选项(或 -Xprint:typer
)with
-语法会产生
// given class maxForDoubles() extends Object(), App.Max[Double] {
// override inline def max(a: Double, b: Double): Double =
// (if a.<(b) then b else a):Double
// }
// final inline given def maxForDoubles: App.maxForDoubles =
// new App.maxForDoubles():App.maxForDoubles
// given class maxForPairs[A >: Nothing <: Any, B >: Nothing <: Any](using
// mA: App.Max[A]
// , mB: App.Max[B]) extends Object(), App.Max[
// Tuple2[maxForPairs.this.A, maxForPairs.this.B]
// ] {
// A
// B
// protected given val mA: App.Max[A]
// protected given val mB: App.Max[B]
// override inline def max(x: Tuple2[maxForPairs.this.A, maxForPairs.this.B]
// ,
// y: Tuple2[maxForPairs.this.A, maxForPairs.this.B]): (A, B) =
// Tuple2.apply[A, B](this.App$maxForPairs$$inline$mA.max(x._1, y._1),
// this.App$maxForPairs$$inline$mB.max(x._2, y._2)
// ):(A, B)
// def App$maxForPairs$$inline$mA: App.Max[A] = maxForPairs.this.mA
// def App$maxForPairs$$inline$mB: App.Max[B] = maxForPairs.this.mB
// }
// final inline given def maxForPairs[A >: Nothing <: Any, B >: Nothing <: Any]
// (
// using mA: App.Max[A], mB: App.Max[B]): App.maxForPairs[A, B] =
// new App.maxForPairs[A, B](using mA, mB)():App.maxForPairs[A, B]
让我们暂时简化 with
-语法,去掉 using
和 maxForDoubles
class maxForPairs[A, B]/*(using mA: Max[A], mB: Max[B])*/ extends Max[(A, B)]:
inline override def max(x: (A, B), y: (A, B)): (A, B) = ??? // (mA.max(x._1, y._1), mB.max(x._2, y._2))
// inline given maxForPairs[A, B]/*(using mA: Max[A], mB: Max[B])*/: maxForPairs[A, B] = new maxForPairs[A, B]
然后在
val inst: maxForPairs[Double, Double] = new maxForPairs[Double, Double]
inst.max((10.0, 3.0), (20.0, -7.0)) // 编译通过
和
val inst: Max[(Double, Double)] = new maxForPairs[Double, Double]
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)
inline given maxForDoubles: Max[Double] = new Max[Double]:
inline override def max(a: Double, b: Double): Double = if a < b then b else a
inline given maxForPairs[A, B](using mA: Max[A], mB: Max[B]): Max[(A, B)] = new Max[(A, B)]:
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
Implementation restriction: nested inline methods are not supported
But with with
-syntax nested inline
are allowed because
inline given maxForDoubles: Max[Double] with
inline override def max(a: Double, b: Double): 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 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
class maxForDoubles extends Max[Double]:
inline override def max(a: Double, b: Double): Double = if a < b then b else a
inline given maxForDoubles: maxForDoubles = new maxForDoubles
class maxForPairs[A, B](using mA: Max[A], mB: Max[B]) extends Max[(A, B)]:
inline override def max(x: (A, B), y: (A, B)): (A, B) = (mA.max(x._1, y._1), mB.max(x._2, y._2))
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
// given class maxForDoubles() extends Object(), App.Max[Double] {
// override inline def max(a: Double, b: Double): Double =
// (if a.<(b) then b else a):Double
// }
// final inline given def maxForDoubles: App.maxForDoubles =
// new App.maxForDoubles():App.maxForDoubles
// given class maxForPairs[A >: Nothing <: Any, B >: Nothing <: Any](using
// mA: App.Max[A]
// , mB: App.Max[B]) extends Object(), App.Max[
// Tuple2[maxForPairs.this.A, maxForPairs.this.B]
// ] {
// A
// B
// protected given val mA: App.Max[A]
// protected given val mB: App.Max[B]
// override inline def max(x: Tuple2[maxForPairs.this.A, maxForPairs.this.B]
// ,
// y: Tuple2[maxForPairs.this.A, maxForPairs.this.B]): (A, B) =
// Tuple2.apply[A, B](this.App$maxForPairs$$inline$mA.max(x._1, y._1),
// this.App$maxForPairs$$inline$mB.max(x._2, y._2)
// ):(A, B)
// def App$maxForPairs$$inline$mA: App.Max[A] = maxForPairs.this.mA
// def App$maxForPairs$$inline$mB: App.Max[B] = maxForPairs.this.mB
// }
// final inline given def maxForPairs[A >: Nothing <: Any, B >: Nothing <: Any]
// (
// using mA: App.Max[A], mB: App.Max[B]): App.maxForPairs[A, B] =
// new App.maxForPairs[A, B](using mA, mB)():App.maxForPairs[A, B]
Let's temporarily simplify the with
-syntax removing using
and maxForDoubles
class maxForPairs[A, B]/*(using mA: Max[A], mB: Max[B])*/ extends Max[(A, B)]:
inline override def max(x: (A, B), y: (A, B)): (A, B) = ??? // (mA.max(x._1, y._1), mB.max(x._2, y._2))
// inline given maxForPairs[A, B]/*(using mA: Max[A], mB: Max[B])*/: maxForPairs[A, B] = new maxForPairs[A, B]
Then the difference between
val inst: maxForPairs[Double, Double] = new maxForPairs[Double, Double]
inst.max((10.0, 3.0), (20.0, -7.0)) // compiles
and
val inst: Max[(Double, Double)] = new maxForPairs[Double, Double]
inst.max((10.0, 3.0), (20.0, -7.0)) // doesn'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] <: 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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论