Scala:Function0 与名称参数
Scala: Function0 vs by-name parameters
谁能就 Scala 编译器如何将别名参数 => T
和 Function0
参数 () => T
相互转换给出明确的答案?我知道它们不一样,但区别非常细微,因为它们可以在许多场景中互换使用。
例子:如果我定义
def someFunction: Int = 2
def f(x: => Int): Unit = println(x)
那我就可以成功调用了
f(2)
f(someFunction)
() => Int
如何成为 => Int
的可接受替代品?
更一般地说,() => T
是普遍接受的别名 => T
参数的替代品吗?
此外,如果我在以下推理上有误,请纠正我:=> T
永远不是 () => T
的可接受替代品,因为第一个是值类型 (T
) ,另一个是函数类型。也就是说,如果我有 def f(x: () => Int)
,我将永远无法传递 Int
或惰性 Int
(因为没有惰性类型,所以无论如何都没有意义) .
好的,这是完整的细目分类。
def value: Int = ???
def method(): Int = ???
def f1(f: () => Int) = ???
def f2(f: => Int) = ???
f1(value) // fails
f1(method) // works
f2(value) // works
f2(method) // works with a warning "empty-paren method accessed as parameterless"
f1(value)
这个失败了,因为 f1
需要一个 Unit => Int 函数,但被赋予了一个 Int 值。
f1(method)
这个之所以有效,是因为 f1
需要一个函数,并且给出了一个方法。区别在于:方法在 Scala 中不是值;它不能单独存在,并且是它在(class、特征、对象等)中定义的上下文的属性。功能是一种价值;它可以保存在一个集合中,作为另一个函数的参数,从一个函数返回等。当编译器需要一个函数并被赋予一个方法时,它会执行 eta 扩展。给定一个函数,例如f: (a: Int, b: Int) => Int
,eta扩展是在保留签名的同时围绕它创建另一层的过程,因此它变成了(a: Int, b: Int) => f(a, b)
。这种技术很有用,因为我们可以将方法转化为函数。给定一些方法 def f(a: Int): Int = ???
,我们可以执行 eta 扩展以从中创建一个 Int => Int 类型的函数:(a: Int) => f(a)
。如果你感兴趣的话,我前段时间写了一篇关于这个的 blog post。
f2(value)
工作正常,但要注意每次在函数体中使用传递的值时都会访问它。
f2(method)
有效,但警告我们正在通过不使用括号调用一个用空括号定义的方法。好的做法是使用不带括号的方法(例如 f
),当它们只表示一个值时,但每次访问时都会重新计算,例如numberOfUpvotes
,并在执行某种副作用时使用带有空括号的方法(例如 f()
),因此该方法不是幂等的,例如createSnapshot()
(同样,这不应该出现在纯功能代码中)。
忠告:不要用什么来代替什么来阻碍你的思想。不要使用替代品。如果某物需要一个功能,就给它提供一个功能。如果它需要一个值,请提供一个值。如果一个方法是在没有括号的情况下定义的,则在没有括号的情况下调用它。如果它有括号,则用括号调用它。
如果您需要从方法转到函数并且编译器需要一个函数,eta 扩展将自动发生。如果它不需要功能,则需要手动执行。
def f(): Int = ???
val a = f // no function context; a is a string
val b: () => Int = f // b is a function Unit => Int
val c = f2 _ // c is a function Unit => Int
最后一个例子是偏应用函数。我觉得我现在说得太宽泛了,所以我会在这里停下来。希望对您有所帮助。
谁能就 Scala 编译器如何将别名参数 => T
和 Function0
参数 () => T
相互转换给出明确的答案?我知道它们不一样,但区别非常细微,因为它们可以在许多场景中互换使用。
例子:如果我定义
def someFunction: Int = 2
def f(x: => Int): Unit = println(x)
那我就可以成功调用了
f(2)
f(someFunction)
() => Int
如何成为 => Int
的可接受替代品?
更一般地说,() => T
是普遍接受的别名 => T
参数的替代品吗?
此外,如果我在以下推理上有误,请纠正我:=> T
永远不是 () => T
的可接受替代品,因为第一个是值类型 (T
) ,另一个是函数类型。也就是说,如果我有 def f(x: () => Int)
,我将永远无法传递 Int
或惰性 Int
(因为没有惰性类型,所以无论如何都没有意义) .
好的,这是完整的细目分类。
def value: Int = ???
def method(): Int = ???
def f1(f: () => Int) = ???
def f2(f: => Int) = ???
f1(value) // fails
f1(method) // works
f2(value) // works
f2(method) // works with a warning "empty-paren method accessed as parameterless"
f1(value)
这个失败了,因为 f1
需要一个 Unit => Int 函数,但被赋予了一个 Int 值。
f1(method)
这个之所以有效,是因为 f1
需要一个函数,并且给出了一个方法。区别在于:方法在 Scala 中不是值;它不能单独存在,并且是它在(class、特征、对象等)中定义的上下文的属性。功能是一种价值;它可以保存在一个集合中,作为另一个函数的参数,从一个函数返回等。当编译器需要一个函数并被赋予一个方法时,它会执行 eta 扩展。给定一个函数,例如f: (a: Int, b: Int) => Int
,eta扩展是在保留签名的同时围绕它创建另一层的过程,因此它变成了(a: Int, b: Int) => f(a, b)
。这种技术很有用,因为我们可以将方法转化为函数。给定一些方法 def f(a: Int): Int = ???
,我们可以执行 eta 扩展以从中创建一个 Int => Int 类型的函数:(a: Int) => f(a)
。如果你感兴趣的话,我前段时间写了一篇关于这个的 blog post。
f2(value)
工作正常,但要注意每次在函数体中使用传递的值时都会访问它。
f2(method)
有效,但警告我们正在通过不使用括号调用一个用空括号定义的方法。好的做法是使用不带括号的方法(例如 f
),当它们只表示一个值时,但每次访问时都会重新计算,例如numberOfUpvotes
,并在执行某种副作用时使用带有空括号的方法(例如 f()
),因此该方法不是幂等的,例如createSnapshot()
(同样,这不应该出现在纯功能代码中)。
忠告:不要用什么来代替什么来阻碍你的思想。不要使用替代品。如果某物需要一个功能,就给它提供一个功能。如果它需要一个值,请提供一个值。如果一个方法是在没有括号的情况下定义的,则在没有括号的情况下调用它。如果它有括号,则用括号调用它。
如果您需要从方法转到函数并且编译器需要一个函数,eta 扩展将自动发生。如果它不需要功能,则需要手动执行。
def f(): Int = ???
val a = f // no function context; a is a string
val b: () => Int = f // b is a function Unit => Int
val c = f2 _ // c is a function Unit => Int
最后一个例子是偏应用函数。我觉得我现在说得太宽泛了,所以我会在这里停下来。希望对您有所帮助。