范围函数适用于/带有/运行/同时/允许:名称从何而来? [英] Scope functions apply/with/run/also/let: Where do the names come from?
问题描述
有很多博客文章(例如 this ),了解标准库函数apply
/with
/run
/also
/let
的用法,这使得区分何时实际使用哪些漂亮库变得更加容易功能.
There are quite a few blog posts (like this) on usages of the standard library functions apply
/with
/run
/also
/let
available that make it a bit easier to distingish when to actually use which of those pretty functions.
For a few weeks now, the official docs even provide guidelines on that topic finally: https://kotlinlang.org/docs/reference/coding-conventions.html#using-scope-functions-applywithrunalsolet
尽管如此,我认为用函数名称记住该函数的个别用例非常困难.我的意思是,对我来说它们似乎是可互换的,为什么let
例如不被称为run
?
Nevertheless, I think it is pretty hard to memorize the function's individual use cases by the function names. I mean, for me they seem to be interchangeable, why isn't let
called run
for instance?
有什么建议吗?我认为名称不是很富于表现力,因此一开始很难看到它们之间的差异.
Any suggestions? I think the names aren't very expressive which makes it hard to see the differences at first.
推荐答案
以下是该名称似乎是如何出现的非官方概述.
Here's an unofficial overview of how the names seem to have come to be.
"let"表达式将函数定义与受限范围相关联
a "let" expression associates a function definition with a restricted scope
在Haskell这样的FP语言中,您可以使用let
将值绑定到像这样的受限范围内的变量
In FP languages like Haskell you can use let
to bind values to variables in a restricted scope like so
aaa = let y = 1+2
z = 4+6
in y+z
Kotlin中的等效代码(尽管过于复杂)将是
The equivalent (albeit overly complicated) code in Kotlin would be
fun aaa() = (1+2).let { y ->
(4+6).let { z ->
y + z
}
}
let
的典型用法是将某些计算的结果绑定到一个范围,而不会污染"外部范围.
The typical usage of let
is to bind the result of some computation to a scope without "polluting" the outer scope.
creater.createObject().let {
if (it.isCorrect && it.shouldBeLogged) {
logger.log(it)
}
}
// `it` is out of scope here
with
with
The with
function is inspired by the with
language construct from languages like Delphi or Visual Basic (and probably many others) where
with关键字是Delphi提供的用于引用的便利 复杂变量的元素,例如记录或对象.
The with keyword is a convenience provided by Delphi for referencing elements of a complex variable, such as a record or object.
myObject.colour := clRed;
myObject.size := 23.5;
myObject.name := 'Fred';
可以重写:
with myObject do
begin
colour := clRed;
size := 23.5;
name := 'Fred';
end;
等效的Kotlin为
with(myObject) {
color = clRed
size = 23.5
name = "Fred"
}
应用
apply
相对较晚地添加到了stdlib中在里程碑阶段(M13).您可以从2015年开始看到此问题,其中用户要求使用确切的功能,甚至建议以后使用名称为应用".
apply
apply
was added to the stdlib relatively late in the milestone phase (M13). You can see this question from 2015 where a user asks for exactly such a function and even suggests the later to-be-used name "apply".
在问题中 https://youtrack.jetbrains.com/issue/KT-6903和 https://youtrack.jetbrains.com/issue/KT-6094 您可以看到有关命名的讨论.提出了诸如build
和init
之类的替代方案,但最终由Daniil Vodopian提出的名称apply
获得了胜利.
In the issues https://youtrack.jetbrains.com/issue/KT-6903 and https://youtrack.jetbrains.com/issue/KT-6094 you can see discussions of the naming. Alternatives like build
and init
were proposed but the name apply
, proposed by Daniil Vodopian, ultimately won.
apply
与with
相似,因为它可用于初始化构造函数之外的对象.在我看来,这就是为什么apply
可能也被命名为with
的原因.但是,由于with
首先被添加到stdlib中,因此Kotlin开发人员决定不破坏现有代码,而是以其他名称添加它.
apply
is similar to with
in that it can be used to initialize objects outside of the constructor. That's why, in my opinion, apply
might as well be named with
. However as with
was added to the stdlib first, the Kotlin devs decided against breaking existing code and added it under a different name.
具有讽刺意味的是,语言Xtend提供了所谓的 with-operator =>
基本上与apply
相同.
Ironically, the language Xtend provides the so-called with-operator =>
which basically does the same as apply
.
also
甚至在以后添加到了stdlib比apply
,即在1.1版中.同样, https://youtrack.jetbrains.com/issue/KT-6903 包含讨论.该功能基本上类似于apply
,除了它使用常规lambda (T) -> Unit
而不是扩展lambda T.() -> Unit
.
also
was added to the stdlib even later than apply
, namely in version 1.1. Again, https://youtrack.jetbrains.com/issue/KT-6903 contains the discussion. The function is basically like apply
except that it takes a regular lambda (T) -> Unit
instead of an extension lambda T.() -> Unit
.
在提议的名称中,有"applyIt","applyLet","on","tap","touch","peek","make".但是也"获胜,因为它不会与任何关键字或其他stdlib函数发生冲突,并且其用法(或多或少)读起来像英语句子.
Among the proposed names were "applyIt", "applyLet", "on", "tap", "touch", "peek", "make". But "also" won as it doesn't collide with any keywords or other stdlib functions and its usages (more or less) read like English sentences.
示例
val object = creater.createObject().also { it.initiliaze() }
读起来有点像
创建者,创建对象,并也初始化它!
其用法读起来有点像英语句子的其他stdlib函数包括 takeIf
和 takeUnless
在1.1版中也添加了.
Other stdlib functions whose usages read a bit like English sentences include takeIf
and takeUnless
which also were added in version 1.1.
最后, run
函数实际上有两个签名.第一个fun <R> run(block: () -> R): R
只需输入一个lambda,然后运行.它主要用于将lambda表达式的结果分配给顶级属性
Finally, the run
function actually has two signatures. The first one fun <R> run(block: () -> R): R
simply takes a lambda and runs it. It is mostly used for assigning the result of a lambda expression to a top-level property
val logger = run {
val name = System.property("logger_name")
Logger.create(name)
}
第二个签名fun <T, R> T.run(block: T.() -> R): R
是一个扩展函数,它以扩展lambda作为参数,并且出于对称性的原因,似乎也被命名为"run".它还可以运行" lambda,但要在扩展接收方的上下文中
The second signature fun <T, R> T.run(block: T.() -> R): R
is an extension function which takes an extension lambda as parameter and seems to also be named "run" for symmetry reasons. It also "runs" a lambda but in the context of an extension receiver
val result = myObject.run {
intitialize()
computeResult()
}
我不知道命名的任何历史原因.
I'm not aware of any historical reasons for the naming.
这篇关于范围函数适用于/带有/运行/同时/允许:名称从何而来?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!