Scala 如何区分 () =>T和=>吨 [英] How does Scala distinguish between () => T and => T

查看:63
本文介绍了Scala 如何区分 () =>T和=>吨的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的其他问题作为重复被关闭,所以我会再试一次.我也读过这个问题,我问的是不同的.我有兴趣学习如何 Call-by-Name: => 的内部实现.类型 不同于 () =>;输入.

My other question got closed as a duplicate, so I'll try this again. I have also read this question and what I'm asking is different. I'm interested in learning the internal implementation of how Call-by-Name: => Type differs from () => Type.

我的困惑来自于查看 javap 和 cfr 反汇编,这表明在两种情况.

My confusion is coming from looking at javap and cfr disassembly which shows no difference in the two cases.

例如ParamTest.scala:

object ParamTest {
  def bar(x: Int, y: => Int) : Int = if (x > 0) y else 10
  def baz(x: Int, f: () => Int) : Int = if (x > 0) f() else 20
}

javap 输出 javap ParamTest.scala:

public final class ParamTest {
  public static int baz(int, scala.Function0<java.lang.Object>);
  public static int bar(int, scala.Function0<java.lang.Object>);
}

CFR 反编译输出 java -jar cfr_0_118.jar ParamTest$.class:

import scala.Function0;

public final class ParamTest$ {
    public static final ParamTest$ MODULE$;

    public static {
        new ParamTest$();
    }

    public int bar(int x, Function0<Object> y) {
        return x > 0 ? y.apply$mcI$sp() : 10;
    }

    public int baz(int x, Function0<Object> f) {
        return x > 0 ? f.apply$mcI$sp() : 20;
    }

    private ParamTest$() {
        MODULE$ = this;
    }
}

编辑 1:Scala 语法树:scalac -Xprint:parse ParamTest.scala

package <empty> {
  object ParamTest extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    };
    def bar(x: Int, y: _root_.scala.<byname>[Int]): Int = if (x.$greater(0))
      y
    else
      10;
    def baz(x: Int, f: _root_.scala.Function0[Int]): Int = if (x.$greater(0))
      f()
    else
      20
  }
}

编辑 2:邮件列表研究:

阅读邮件列表上的这篇有趣的帖子本质上说 =>T 被实现为 () =>;.引用:

Read this interesting post on the mailing list which essentially states that => T is implemented as () => T. Quote:

先看

f: =>布尔值

虽然这被称为按名称参数",但它实际上是作为一个Function0来实现的,

Although this is called a "by-name parameter", it is actually implemented as a Function0,

f: () =>布尔值

只是在两端使用不同的语法.

just with different syntax used on both ends.

现在我对这个答案更加困惑,它明确指出两者是不同的.

Now I'm even more confused by this answer which explicitly states that the two are different.

问题:

  • Scala 如何区分 barbaz?两者的方法签名(不是实现)在反编译代码中是相同的.
  • 这两种情况的差异是否不会持久化到已编译的字节码中?
  • 反编译的代码是否不准确?
  • 在编辑 1 之后添加:我发现 scalac 语法树确实显示出不同,bar 具有 _root_.scala 类型的第二个参数.按名称>[Int].它有什么作用?任何解释、scala 源 或等效伪代码中的指针都会有所帮助.
  • 参见上面的编辑 2:引用的块是否正确?如在,是 =>T Function0 的特殊子类?
  • How is Scala distinguishing bar from baz? The method signatures (not implementation) for both are identical in the decompiled code.
  • Does the difference in the two scenarios not get persisted into the compiled bytecode?
  • Is the decompiled code inaccurate?
  • Added after Edit 1: I found that the scalac syntax tree does show a difference, bar has the second argument of type _root_.scala.<byname>[Int]. What does it do? Any explanation, pointers in scala source or equivalent pseudo code will be helpful.
  • See EDIT 2 above: Is the quoted block correct? As in, is => T a special subclass of Function0?

推荐答案

Scala 如何区分 barbaz?方法签名(不是实现)在反编译代码中是相同的.

How is Scala distinguishing bar from baz? The method signatures (not implementation) for both are identical in the decompiled code.

Scala 不需要区分两者.从它的角度来看,这是两种不同的方法.有趣的是(至少对我而言),如果我们将 baz 重命名为 bar 并尝试使用按名称调用"参数创建重载,我们会得到:

Scala doesn't need to distinguish between the two. From it's perspective, these are two different methods. What's interesting (to me at least) is that if we rename baz into bar and try to create an overload with a "call-by-name" parameter, we get:

Error:(12, 7) double definition:
method bar:(x: Int, f: () => Int)Int and
method bar:(x: Int, y: => Int)Int at line 10
have same type after erasure: (x: Int, f: Function0)Int
  def bar(x: Int, f: () => Int): Int = if (x > 0) f() else 20

这是对我们的一个暗示,在幕后,Function0 的翻译正在进行中.

Which is a hint for us that under the covers, something is going on with the translation to Function0.

这两种情况的差异是否不会持久化到编译的字节码?

Does the difference in the two scenarios not get persisted into the compiled bytecode?

在 Scala 发出 JVM 字节码之前,它有额外的编译阶段.在这种情况下,有趣的是查看uncurry"阶段 (-Xprint:uncurry):

Before Scala emits JVM bytecode, it has additional phases of compilation. An interesting one in this case is to look at the "uncurry" stage (-Xprint:uncurry):

[[syntax trees at end of uncurry]] 
package testing {
  object ParamTest extends Object {
    def <init>(): testing.ParamTest.type = {
      ParamTest.super.<init>();
      ()
    };
    def bar(x: Int, y: () => Int): Int = if (x.>(0))
      y.apply()
    else
      10;
    def baz(x: Int, f: () => Int): Int = if (x.>(0))
      f.apply()
    else
      20
  }
}

甚至在我们发出字节码之前,bar 就被翻译成一个 Function0.

Even before we emit byte code, bar is translated into a Function0.

反编译的代码不准确

不,它绝对准确.

我发现 scalac 语法树确实显示了差异,bar 有root.scala.[Int] 类型的第二个参数.它有什么作用做什么?

I found that the scalac syntax tree does show a difference, bar has the second argument of type root.scala.[Int]. What does it do?

Scala 编译是分阶段完成的,其中每个阶段的输出都是下一个阶段的输入.除了解析后的 AST,Scala 阶段还创建符号,这样,如果一个阶段依赖于特定的实现细节,它将使其可用.<byname> 是一个编译器符号,它表明此方法使用按名称调用",以便其中一个阶段可以看到并对其进行处理.

Scala compilation is done in phases, where each phases output is the input to the next. In addition to the parsed AST, Scala phases also create symbols, such that if one stage relys on a particular implementation detail it will have it available. <byname> is a compiler symbol which shows that this method uses "call-by-name", so that one of the phases can see that and do something about it.

这篇关于Scala 如何区分 () =>T和=>吨的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆