21

有人说每种编程语言都有它的“复杂度预算”,它可以用来完成它的目的。但是,如果复杂性预算被耗尽,每一个微小的变化都会变得越来越复杂,并且难以以向后兼容的方式实现。

从 2010 年 8 月开始阅读 Lambda 的当前临时语法(≙ Lambda 表达式、异常透明度、防御方法和方法引用)后,我想知道 Oracle 的人们在考虑此类更改时是否完全忽略了 Java 的复杂性预算。

这些是我正在考虑的问题——其中一些更多的是关于语言设计的:

  • 提议的添加在复杂性上是否与其他语言选择的方法相当?
  • 通常是否可以将此类添加添加到语言中保护开发人员免受实现的复杂性?
  • 这些添加是否标志着 Java 作为一种语言的演变已经走到了尽头,还是在改变一门历史悠久的语言时这是预期的?
  • 在语言进化的这一点上,其他语言是否采取了完全不同的方法?

谢谢!

4

5 回答 5

3

对一些范围消歧结构取模,几乎所有这些方法都遵循 lambda 抽象的实际定义:

λx.E

按顺序回答您的问题:

我不认为有什么特别的事情可以使 Java 社区的提案比其他任何事情更好或更差。正如我所说,它遵循数学定义,因此所有忠实的实现都将具有几乎完全相同的形式。

固定在命令式语言上的匿名一流函数往往最终成为一些程序员喜欢和经常使用的特性,而另一些程序员则完全忽略——因此,给它一些不会混淆这类人的语法可能是一个明智的选择谁选择忽略此特定语言功能的存在。我认为隐藏实现的复杂性和细节是他们试图通过使用与 Java 很好融合的语法来做的事情,但对 Java 程序员没有真正的内涵。

他们可能希望使用一些不会使现有定义复杂化的语法,因此他们可以选择用作运算符等的符号受到轻微限制。当然,Java 对保持向后兼容的坚持稍微限制了语言的发展,但我不认为这一定是一件坏事。PHP 方法处于光谱的另一端(即“每次有新的点发布时,让我们打破一切!”)。我不认为 Java 的发展在本质上受到限制,除非它的设计的一些基本原则 - 例如遵守 OOP 原则,基于 VM。

我认为很难从 Java 的角度对语言演变做出强有力的陈述。它处于一个相当独特的位置。一方面,它非常非常受欢迎,但它相对较旧。微软在决定开始设计一种名为“C#”的语言之前,已经受益于至少 10 年的 Java 遗产。C 编程语言基本上完全停止了发展。C++ 几乎没有任何被主流接受的重大变化。Java 通过一个缓慢但一致的过程继续发展——如果有的话,我认为它比任何其他安装了同样庞大的代码库的语言都更有能力继续发展。

于 2010-08-13T10:54:50.250 回答
3

我没有关注 Java 7 lambda 提案的过程和演变,我什至不确定最新的提案措辞是什么。将此视为咆哮/意见,而不是事实陈述。此外,我已经很久没有使用 Java,所以语法可能在某些地方生锈和不正确。

首先,什么是 Java 语言的 lambda?语法糖。虽然通常 lambdas 使代码能够在适当的位置创建小型函数对象,但这种支持已经在 J​​ava 语言中通过使用内部类预先设置——在某种程度上。

那么 lambda 的语法有多好呢?它在哪些方面优于以前的语言结构?哪里可以更好?

对于初学者来说,我不喜欢 lambda 函数有两种可用语法这一事实(但这属于 C# 行,所以我想我的观点并不普遍。我想如果我们想粉饰一下,那么 #(int x)(x*x)#(int x){ return x*x; }即使双重语法没有添加任何其他东西。我更喜欢第二种语法,更通用,但需要额外的写作成本return;短版本。

为了真正有用,lambda 可以从定义它们的范围和闭包中获取变量。与内部类一致,lambda 仅限于捕获“有效最终”变量。与该语言以前的特性保持一致是一个很好的特性,但是为了甜蜜,能够捕获可以重新分配的变量会很好。为此,他们正在考虑将通过引用@Shared捕获上下文中存在并带有注释的变量,允许分配。对我来说,这似乎很奇怪,因为 lambda 如何使用变量是在声明变量的位置而不是定义 lambda 的位置确定的。一个变量可以在多个 lambda 中使用,这会在所有 lambda 中强制执行相同的行为。

Lambda 试图模拟实际的函数对象,但该提议并未完全实现:为了保持解析器简单,因为到目前为止,标识符表示对象或方法已保持一致,并且调用 lambda 需要!在lambda 名称:#(int x)(x*x)!(5)将返回25. 这带来了一种用于 lambda 的新语法,该语法不同于其他语言,其中 以!某种方式作为.execute虚拟通用接口的同义词,Lambda<Result,Args...>但是,为什么不让它完整呢?

Lambda可以创建一个新的通用(虚拟)接口。它必须是虚拟的,因为接口不是真正的接口,而是这样的系列:Lambda<Return>, Lambda<Return,Arg1>, Lambda<Return,Arg1,Arg2>... 他们可以定义一个执行方法,我希望它像 C++ 一样operator(),但如果这是一个负担,那么任何其他名称都可以,将其!作为方法执行的快捷方式:

 interface Lambda<R> {
    R exec();
 }
 interface Lambda<R,A> {
    R exec( A a );
 }

然后编译器只需要翻译identifier!(args)identifier.exec( args ),这很简单。lambda 语法的翻译需要编译器识别正在实现的正确接口,并且可以匹配为:

 #( int x )(x *x)
 // translated to
 new Lambda<int,int>{ int exec( int x ) { return x*x; } }

这也将允许用户在更复杂的情况下定义可用作 lambdas 的内部类。例如,如果 lambda 函数需要以@Shared只读方式捕获带注释的变量,或在捕获位置维护捕获对象的状态,则可以使用 Lambda 的手动实现:

 new Lambda<int,int>{ int value = context_value;
     int exec( int x ) { return x * context_value; }
 };

以类似于当前内部类定义的方式,因此对当前 Java 用户来说是自然的。例如,这可以在循环中用于生成乘数 lambda:

 Lambda<int,int> array[10] = new Lambda<int,int>[10]();
 for (int i = 0; i < 10; ++i ) {
    array[i] = new Lambda<int,int>{ final int multiplier = i;
       int exec( int x ) { return x * multiplier; }
    };
 }
 // note this is disallowed in the current proposal, as `i` is
 // not effectively final and as such cannot be 'captured'. Also
 // if `i` was marked @Shared, then all the lambdas would share
 // the same `i` as the loop and thus would produce the same
 // result: multiply by 10 --probably quite unexpectedly.
 //
 // I am aware that this can be rewritten as:
 // for (int ii = 0; ii < 10; ++ii ) { final int i = ii; ...
 //
 // but that is not simplifying the system, just pushing the
 // complexity outside of the lambda.

这将允许使用新的简单语法的 lambda 和接受 lambda 的方法:#(int x){ return x*x; }或者在糖衣干扰预期语义的特定情况下使用更复杂的手动方法。

总的来说,我相信 lambda 提案可以在不同的方向进行改进,它添加语法糖的方式是一种泄漏抽象(您已经在外部处理了 lambda 特有的问题),并且不提供较低级别的接口它在不完全适合简单用例的用例中,使用户代码的可读性降低。:

于 2010-08-16T10:17:18.393 回答
1

它并不比其他语言中的 lambda 表达式复杂多少。

考虑...

int square(x) {
    return x*x;
}

爪哇:

#(x){x*x}

Python:

lambda x:x*x

C#:

x => x*x

我认为 C# 方法更直观一些。个人比较喜欢...

x#x*x
于 2010-08-05T12:20:53.553 回答
1

也许这不是您问题的真正答案,但这可能与objective-c(与Java相比当然具有非常狭窄的用户群)通过块(示例)扩展的方式相媲美。虽然语法不适合语言的其余部分(恕我直言),但它是一个有用的补充,并且在语言特性方面增加的复杂性得到了回报,例如并发编程的复杂性较低(简单的事情,如数组上的并发迭代或复杂的技术,如Grand Central Dispatch)。

此外,使用块时,许多常见任务更简单,例如使一个对象成为同一类的多个实例的委托(或 - 在 Java 术语中 - “侦听器”)。在 Java 中,匿名类已经可以用于这个目的,所以程序员知道这个概念,并且可以使用 lambda 表达式节省几行源代码。

在 Objective-c(或 Cocoa/Cocoa Touch 框架)中,现在通常只能使用块来访问新功能,而且程序员似乎正在迅速采用它(考虑到他们必须放弃与旧操作系统版本的向后兼容性)。

于 2010-08-15T17:58:01.143 回答
1

这真的非常接近于新一代 C++ (C++0x) 中提出的 Lambda 函数,所以我认为,Oracle 人在编写自己的实现之前已经查看了其他实现。

http://en.wikipedia.org/wiki/C%2B%2B0x

[](int x, int y) { return x + y; }
于 2010-08-16T06:16:16.590 回答