【问题标题】:Understanding how any function definition in scala evaluates to a trait了解 scala 中的任何函数定义如何评估为特征
【发布时间】:2017-01-18 09:01:27
【问题描述】:

我目前正在研究几个 scala 代码库(每个代码库由不同地理位置的不同人员维护),我觉得 Scala 提供了不同的方法来做同样的事情。我的第一手理解是 Scala 有一层相当厚的语法糖代码,所以我现在的直接目标是深入那层,看看基本的构建块是什么。

在阅读了一些文档后,我了解到,scala 中的每个函数定义基本上都是一个带有填充“应用”方法的特征。(类似于 java 中与匿名主体/类的接口)。以此为基础(如果我的基本理解本身是错误的,请纠正我),我正在使用 Scala 中的函数定义。

考虑一下:

  def func (s:String):Int = {
    100
  }

评估为

  new Function1[String, Int] {
    def apply(s:String):Int = {
      100
    }
  }

但是,以下对特征定义的评估是什么(以及如何)?

 def func (body: => Boolean):Int = {
    body;
    100;
 }

在这种情况下,“new Function ...”代码是什么样的?

【问题讨论】:

    标签: scala function traits


    【解决方案1】:

    如果出现混淆,请查看javap 输出。 Scala 的 REPL 提供了方便的检查方式:

    scala>  def func (body: => Boolean):Int = {
         |     body;
         |     100;
         |  }
    func: (body: => Boolean)Int
    
    scala> :javap -c func
    Compiled from "<console>"
    public class  {
      public static final  MODULE$;
    
      public static {};
        Code:
           0: new           #2                  // class 
           3: invokespecial #12                 // Method "<init>":()V
           6: return
    
      public int func(scala.Function0<java.lang.Object>);
        Code:
           0: aload_1
           1: invokeinterface #20,  1           // InterfaceMethod scala/Function0.apply$mcZ$sp:()Z
           6: pop
           7: bipush        100
           9: ireturn
    
      public ();
        Code:
           0: aload_0
           1: invokespecial #24                 // Method java/lang/Object."<init>":()V
           4: aload_0
           5: putstatic     #26                 // Field MODULE$:L;
           8: return
    }
    

    所以在这里您可以看到body: =&gt; Boolean 在字节码中表示为scala.Function0&lt;java.lang.Object&gt;

    我在这里使用scala 2.11.8 版本。 Scala 2.12 将尝试尽可能多地重用 java8 的功能,例如默认方法(在接口中)或 SAM(您可以尝试 -Xexperimental 标志,它为 2.11 启用 introp)。这将导致另一个字节码(与 java 2.12更改here的信息。

    【讨论】:

    • 感谢 dveim!那是有见地的。那么“新功能......”代码会是什么样子呢?
    • @soupybionics 类似val body = new Function0[Boolean] { def apply(): Boolean = println("calling body"); true },类似Function1func。请注意,: =&gt; 语法(也称为按名称调用)只不过是 function that takes 0 args and returns something
    • 查看字节码并不能真正解释类型会发生什么。你不能用Function0定义它,它会有不同的类型。
    • @dveim 不,类型不同。接受 0 个参数并返回某些内容的函数将是 () =&gt; Something,您必须使用括号以不同的方式调用它。
    • @VictorMoroz 我的意思是x : =&gt; Somethingx: () =&gt; Something 之间没有区别。事实上,除了括号,它们不能应用于: =&gt;,并且(强制评估)必须应用于() =&gt; Something。您可以使用javap -c 验证这一点:pastebin.com/g7gub10p
    【解决方案2】:

    首先

    def func (s:String):Int = {
      100
    }
    

    不是一个函数,它是一个方法,它没有任何价值,并且它本身不会评估任何东西。当放置在函数上下文中时,它确实扩展为一个函数,但扩展为另一个函数(Scala 创建了一个包装器):

    new Function1[String, Int] {
      def apply(s:String):Int = {
        func(s)
      }
    }
    

    因此,如果您想进行 eta-expand(Scala 将方法转换为函数的术语)

    def func (body: => Boolean):Int = {
      body;
      100;
    }
    

    应该是这样的:

    new Function1[=> Boolean, Int] {
      def apply(body: => Boolean):Int = {
        func(body)
      }
    }
    

    Scala 将类型显示为:

    (=> Boolean) => Int
    

    但这里的问题是,按名称类型不能用作类型参数,因此使用Function1 trait 来定义这个函数似乎没有合法的方法。

    更新

    看起来有一个使用 lambda 语法的解决方法(虽然我不能明确声明 body 的类型,但不允许使用名称):

    var expandedFunc: (=> Boolean) => Int = body => func(body)
    

    【讨论】:

      【解决方案3】:

      在 Dviem 的 cmets 的帮助下,代码应该如下所示:

      val func = new Function1[()=>Boolean, Int] {
                   def apply(fn:()=>Boolean):Int = { fn(); 100 }
                 }
      
      >>> func: (() => Boolean) => Int = <function1>
      
      func (new Function0[Boolean] {
              def apply():Boolean  = { println ("returning true"); true }
            })
      
      >>> returning true
      >>> res0: Int = 100
      

      【讨论】:

        猜你喜欢
        • 2019-05-03
        • 2021-06-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-05
        相关资源
        最近更新 更多