作用域函数 apply/with/run/also/let:它们的名字从何而来?

2024-05-04

有很多博客文章(例如this https://dzone.com/articles/examining-kotlins-also-apply-let-run-and-with-intentions)关于标准库函数的用法apply/with/run/also/let可以让我们更容易区分何时实际使用这些漂亮的函数中的哪些。

几周来,官方文档甚至最终提供了有关该主题的指南:https://kotlinlang.org/docs/reference/coding-conventions.html#using-scope-functions-applywithrunalsolet https://kotlinlang.org/docs/reference/coding-conventions.html#using-scope-functions-applywithrunalsolet

尽管如此,我认为记住这个函数是相当困难的个别用例 by the 函数名。我的意思是,对我来说它们似乎可以互换,为什么不可以let called run例如?

有什么建议么?我认为这些名称不太具有表现力,这使得一开始很难看出差异。


这是一个非官方概述这些名称的由来。

let

let https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/let.html受到函数式编程世界的启发。根据维基百科 https://en.wikipedia.org/wiki/Let_expression

“let”表达式将函数定义与受限范围相关联

在像 Haskell 这样的 FP 语言中,你可以使用let将值绑定到受限范围内的变量,如下所示

aaa = let y = 1+2
          z = 4+6
          in  y+z

Kotlin 中的等效(尽管过于复杂)代码是

fun aaa() = (1+2).let { y -> 
              (4+6).let { z ->
                y + z
              } 
            }

典型用法let是将某些计算的结果绑定到一个范围而不“污染”外部范围。

creater.createObject().let {
    if (it.isCorrect && it.shouldBeLogged) {
        logger.log(it)
    }
}

// `it` is out of scope here

with

The with https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/with.html函数的灵感来自于with语言由像这样的语言构造Delphi http://www.delphibasics.co.uk/RTL.asp?Name=with or 视觉基础 https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/with-end-with-statement(可能还有许多其他人)在哪里

with关键字是Delphi提供的一种方便引用的方法 复杂变量的元素,例如记录或对象。

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

apply https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/apply.html在里程碑阶段 (M13) 相对较晚的时间被添加到 stdlib。你可以看到this https://stackoverflow.com/a/28659271/6153062015 年的一个问题,用户要求提供这样的功能,甚至建议稍后使用的名称“apply”。

在问题中https://youtrack.jetbrains.com/issue/KT-6903 https://youtrack.jetbrains.com/issue/KT-6903 and https://youtrack.jetbrains.com/issue/KT-6094 https://youtrack.jetbrains.com/issue/KT-6094你可以看到命名的讨论。替代品如build and init被提议,但名称apply丹尼尔·沃多皮安 (Daniil Vodopian) 提出的方案最终获胜。

apply类似于with因为它可以用来初始化构造函数之外的对象。这就是为什么,在我看来,apply不妨命名with。然而作为with首先被添加到 stdlib 中,Kotlin 开发人员决定不破坏现有代码并以不同的名称添加它。

讽刺的是,Xtend 语言提供了所谓的带运算符=> http://jnario.org/org/jnario/spec/tests/integration/UsingXtendSWithOperatorSpec.html这基本上与apply.

also

also https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/also.html甚至晚于apply,即1.1版本中。再次,https://youtrack.jetbrains.com/issue/KT-6903 https://youtrack.jetbrains.com/issue/KT-6903包含讨论。该功能基本上是这样的apply除了它需要一个常规的 lambda(T) -> Unit而不是扩展 lambdaT.() -> Unit.

提议的名称包括“applyIt”、“applyLet”、“on”、“tap”、“touch”、“peek”、“make”。但“also”获胜,因为它不与任何关键字或其他 stdlib 函数冲突,而且它的用法(或多或少)读起来像英语句子。

Example

val object = creater.createObject().also { it.initiliaze() }

读起来有点像

创建者,创建对象并also初始化它!

其他 stdlib 函数的用法读起来有点像英语句子,包括takeIf https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/take-if.html and takeUnless https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/take-unless.html1.1 版本中也添加了这些内容。

run

最后,run https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/run.html函数实际上有两个签名。第一个fun <R> run(block: () -> R): R只需要一个 lambda 并且runs它。它主要用于将 lambda 表达式的结果分配给顶级属性

val logger = run {
    val name = System.property("logger_name")
    Logger.create(name)
}

第二个签名fun <T, R> T.run(block: T.() -> R): R是一个扩展函数,它采用扩展 lambda 作为参数,并且出于对称原因似乎也被命名为“run”。它也“运行”一个 lambda,但是是在扩展接收器的上下文中

val result = myObject.run {
    intitialize()
    computeResult()
}

我不知道这个命名有任何历史原因。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

作用域函数 apply/with/run/also/let:它们的名字从何而来? 的相关文章

随机推荐