我试图理解名字类型注释在高阶函数的上下文中的含义。这是一个例子:
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
在其参数中是严格的(即它没有=>
注释),所以为什么不强制它的参数(在这种情况下是???
)并引发一个异常?
这里有没有描述语义的论文?
我在这里寻找权威的答案。
答案 0 :(得分:5)
f
函数中的参数apply
是Funcion1
,它采用A
类型的按名称调用参数并返回B
。因此,f(x)
不会评估按名称调用参数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隐式将call-by-name或“thunk”参数视为不带参数的函数)。您可以再次验证这一点:
def const[A](ignored: A): Int = if (ignored.isInstanceOf[Function0[_]]) 1 else 0
现在Test.res
会为您1
(意味着ignored
确实是Function0
)。
因此,要以不同的方式回答问题,{{1}}有一个类型为const
的急切参数,但这并不重要,因为A
在您的示例中成为函数,并且你永远不会应用这个功能,因此永远不会执行A
。
Scala中有some debate why there is both一个“thunk”或无括号的函数和一个空的函数(???
)。
答案 1 :(得分:0)
我正在打开另一个答案,因为你似乎还不清楚发生了什么:
通常严格属性是根据所应用的函数(在本例中为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
的定义并不重要。重要的是x
的{{1}}参数是按名称(或上一版本中的函数)。因此,apply
使用函数参数 f(x)
调用f
,因此无论x
的正文是什么,此时都不会对此进行评估。< / p>