【问题标题】:How does by-name arguments work in higher-order functions?名称参数在高阶函数中如何工作?
【发布时间】:2013-04-18 23:02:17
【问题描述】:

我试图理解在高阶函数的上下文中按名称类型注释的含义。这是一个例子:

object Test { 
  def apply[A, B](f: (=> A) => B, x: => A): B = f(x) 
  def const[A](ignored: A): Int = 1
  val res: Int = apply(const, ???)
}

const 在其参数中是严格的(即它缺少 => 注释),那么为什么不强制其参数(在本例中为 ???)并引发异常?

这里有没有描述语义的论文?

我在这里寻找权威答案。

【问题讨论】:

    标签: scala


    【解决方案1】:

    apply 函数中的参数f 是一个Funcion1,它采用A 类型的按名称调用参数并返回一个B。因此,f(x) 不会评估 call-by-name 参数x,而是将其引用直接传递给f

    理解res如下:

    def res[C]: Int = apply[C, Int](const, ???)
    

    在您的示例中,C 将是一个非特定类型。现在在这一行中为const 推断的类型参数是什么?它是=> C。不幸的是,您不能输入该参数:

    def res[C]: Int = apply[C, Int](const[=> C], ???)  // illegal syntax
    

    但你可以验证发生了什么:

    def res[C]: Int = apply[C, Int](const[Nothing], ???)
    

    给你

    <console>:10: error: type mismatch;
     found   : Nothing => Int
     required: => C => Int
             def res[C]: Int = apply[C, Int](const[Nothing], ???)
                                                  ^
    

    这种类型在const 中作为Function0[Int] 出现(因此Scala 隐含地将按名称调用或“thunk”参数视为没有参数的函数)。您可以再次验证这一点:

    def const[A](ignored: A): Int = if (ignored.isInstanceOf[Function0[_]]) 1 else 0
    

    现在Test.res 将给你1(意味着ignored 确实是Function0)。


    所以要以不同的方式回答这个问题,const 有一个 A 类型的热切参数,但这并不重要,因为 A 在您的示例中变成了一个函数,而您永远不会应用该函数,因此??? 永远不会被执行。


    在 Scala 中有 some debatewhy there is both 一个“thunk”或无括号函数和一个空括号函数 (Function0)。

    【讨论】:

    • 感谢您的详细解释。 Scala 中的按需语义(我误认为是别名)是否在某处定义?通常,严格属性是根据所应用的函数(在本例中为 const)定义的,但您的解释主要是关于实现以及 const 的参数是如何构造的。
    • 另一个问题,类型参数A可以同时与按值参数(例如Int)和按需参数(即=&gt; Int)统一吗?
    • @tibbe:我不熟悉“按需要”这个术语,但区分 Scala 的按名称参数和惰性求值参数很重要。后者被评估 0 次或 1 次,而前者被评估 0 次或多次 次。
    • "Call-by-need is a memoized version of call-by-name" - 换句话说,按需要对应于 Scala 的 lazy 值。
    • @tibbe :按名称参数(请参阅我之前关于按需要的评论!)确实统一了两者。如果您需要在函数体中多次使用该值,您很可能会使用 memoisation(将参数分配给 lazy val)。另见this questionthat question
    【解决方案2】:

    我正在打开另一个答案,因为您似乎仍然不清楚发生了什么:

    通常严格属性是根据所应用的函数(在本例中为 const)定义的,但您的解释主要是关于实现以及如何构造 const 的参数。

    我将用不同的类型名称重述您的对象:

    object Test { 
      def apply[A, B](f: (=> A) => B, x: => A): B = f(x) 
      def const[C](ignored: C): Int = 1
      def res[A1]: Int = apply[A1, Int](const, ???)
    }
    

    现在,让我们与Function0 交换名称参数,以突出隐藏“惰性”的位置:

    object Test { 
      def apply[A, B](f: (() => A) => B, x: () => A): B = f(x) 
      def const[C](ignored: C): Int = 1
      def res[A1]: Int = apply[A1, Int](const[() => A1], () => ???)
    }
    

    你看,const 的定义并不重要。重要的是 applyx 参数是按名称命名的(或上一个版本中的函数)。所以f(x) 使用函数参数 x 调用f,所以无论x 的主体是什么,此时都不会对其进行评估。

    【讨论】:

      猜你喜欢
      • 2018-07-27
      • 1970-01-01
      • 1970-01-01
      • 2019-09-22
      • 1970-01-01
      • 2020-04-29
      • 1970-01-01
      • 2015-09-19
      • 1970-01-01
      相关资源
      最近更新 更多