Scala 2.8集合库是否成为“历史上最长的自杀声明”?

873

我刚开始研究即将发布的2.8版本中的Scala集合库重新实现。熟悉2.7版本库的人会注意到,从使用角度来看,该库几乎没有变化。例如...

> List("Paris", "London").map(_.length)
res0: List[Int] List(5, 6)

这将适用于任何版本。该库非常易于使用:事实上,它非常棒。然而,以前不熟悉Scala并且四处摸索以了解语言的感觉者现在必须理解类似于以下方法签名:

def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That

对于这样简单的功能来说,这是一个令人望而生畏的签名,我发现自己很难理解。 不是因为我认为Scala有可能成为下一个Java(或/C/C++/C#) - 我不相信它的创造者的目标是那个市场 - 但我认为Scala成为下一个Ruby或Python(即获得重要商业用户群)肯定是可行的。

  • 这会让人们不喜欢使用Scala吗?
  • 这会让Scala在商业世界中声名狼藉,成为只有专门的博士生才能理解的学术玩物吗? CTO和软件负责人会被吓到吗?
  • 库的重新设计是明智的决定吗?
  • 如果你正在商业上使用Scala,你是否担心这个问题?你计划立即采用2.8还是等待看看会发生什么?

Steve Yegge 曾经批评过 Scala(我认为是错误的),因为他认为它的类型系统过于复杂。我担心有人会利用这个API散布FUD,就像Josh Bloch吓坏了JCP不添加Java闭包一样。

注意 - 我应该明确表示,尽管我相信Joshua Bloch在拒绝BGGA闭包提案方面具有影响力,但我并不将其归因于除了他真诚持有的信念之外的任何事情,即该提案代表了一个错误。


尽管我的妻子和同事不断告诉我,但我并不认为自己是个白痴:我拥有牛津大学的数学学位,并且已经在商业领域编程近12年,在Scala方面有一年的商业经验。 < p > 请注意,这个具有煽动性的主题标题是关于1980年代初英国政党宣言的引语。这个问题是主观的,但它是一个真正的问题,我已将其设为CW,并希望得到一些意见。


10
"FUD"只是代表“恐惧、不确定性和怀疑”-我认为这很清楚地表达了Josh Bloch的演讲的基调,我也同意他的论点和推理等等。如果您查看编辑记录,您会发现我最初没有使用"FUD",因为我不想暗示负面含义。 - oxbow_lakes
32
这个问题曾在Martin Odersky的Scala Days 2010的开场演讲中提到。http://days2010.scala-lang.org/node/136 - Binil Thomas
7
我喜欢 Scala 的原因在于,即使不理解其复杂的类型系统,也可以做出简单而优雅的事情。它的语法可能令人望而生畏,但有一件事是确定的,就是没有“魔法”,例如魔法是语言的一部分,这是一种非常勇敢和聪明的方法。你可以用这种语言来创建新的DSL和新的迷你语言,在错误的手中,Scala 可能只是意大利晚餐中的美味佳肴,但一旦熟悉了它,它就是一门令人惊叹的语言。 - Eran Medan
88
这个问题如何能被标记为“无建设性”,当它导致@MartinOdersky重新评估Scala的可用性,并使其文档系统隐藏类型系统细节,更不用说引人入胜的讨论了呢? - Jerry101
14
确实,SO只适用于正确格式的技术性问题。如果您有一些微妙、有趣和深远的内容,请寻找其他地方。官僚心态万岁。 - qed
显示剩余11条评论
18个回答

890
我希望这不是一份"自杀信",但我能理解你的观点。你提到了Scala的一个优点和问题:它的可扩展性。这使得我们可以在库中实现大部分主要功能。在其他一些语言中,像map或collect这样的序列操作已经内置了,没有人需要看到编译器为了让它们平稳工作所需付出的努力。而在Scala中,所有这些都在一个库中,因此是公开的。
事实上,map函数的复杂类型支持的功能相当高级。考虑以下内容:
scala> import collection.immutable.BitSet
import collection.immutable.BitSet

scala> val bits = BitSet(1, 2, 3)
bits: scala.collection.immutable.BitSet = BitSet(1, 2, 3)

scala> val shifted = bits map { _ + 1 }
shifted: scala.collection.immutable.BitSet = BitSet(2, 3, 4)

scala> val displayed = bits map { _.toString + "!" }
displayed: scala.collection.immutable.Set[java.lang.String] = Set(1!, 2!, 3!)
看看你总是得到最好的类型?如果将 Int 映射到 Int,则再次获得 BitSet,但如果将 Int 映射到 String,则会获得通用 Set。map 函数的静态类型和运行时表示都取决于传递给它的函数的结果类型。即使集合为空,这也适用,因此永远不会应用该函数!据我所知,没有其他具有等效功能的集合框架。但就用户而言,事情应该是这样工作的。
我们面临的问题是所有使这种情况发生的聪明技术都泄漏到类型签名中,这使得它们变得复杂和可怕。但是也许默认情况下用户不应该被显示 map 的完整类型签名?如果她在 BitSet 中查找 map,她会得到什么:
map(f: Int => Int): BitSet     (click here for more general type)

在这种情况下,文档不会撒谎,因为从用户的角度来看,map确实具有类型 (Int => Int) => BitSet。但是,map还具有更通用的类型,可以通过单击另一个链接来检查。

我们尚未在我们的工具中实现此类功能。但我认为我们需要这样做,以避免吓到人们并提供更有用的信息。有了这样的工具,希望聪明的框架和库不会变成自杀注释。


107
谢谢你抽出时间回复我的问题。看到这些回答,我觉得不用担心。会有足够多不被吓倒的人参加。我感觉自己像个调皮的小学生! - oxbow_lakes
164
不,我认为你完全正确地指出了那一点。除非我们采取一些措施,否则其他人会感到害怕。 - Martin Odersky
33
马丁,我喜欢你的建议:展示简化的方法签名,并通过链接隐藏其他的细节。 - Derek Mahar
18
我认为至少同样有效的解决方案是在文档中提供更多的解释。如果大部分方法(甚至大部分类)没有超过一句话来描述它们的目的和操作,那么我不会感到签名很令人生畏。 - Nick Johnson
99
更新:最终版本的Scala 2.8有一个类似我描述的机制。如果您在scaladocs中查找BitSet,则会发现以下内容:def map[B](f: (Int) ⇒ B): BitSet[B][用例] 通过将函数应用于此位集的所有元素来构建新集合。 - Martin Odersky
显示剩余14条评论

226

我没有博士学位,也没有任何计算机科学、数学或其他领域的学位。我没有使用过Scala或任何类似的语言。我没有任何与之相似的类型系统经验。事实上,我唯一比较熟悉的语言是Pascal,它的类型系统并不是很复杂(虽然它有范围类型,据我所知,几乎没有其他语言有这个特性,但这在这里并不是很相关)。我还会另外三种语言:BASIC、Smalltalk和Ruby,这些语言都没有类型系统。

然而,我完全没有问题理解你发布的map函数的签名。在我看来,它看起来几乎与我见过的其他语言中map的签名相同。不同之处在于,这个版本更加通用。它看起来更像是C++ STL的东西,而不是Haskell。特别是,它通过仅要求参数为IterableLike来抽象出具体的集合类型,并通过仅要求存在可以从结果值集合中构建某些东西的隐式转换函数来抽象出具体的返回类型。是的,这很复杂,但它确实只是泛型编程通用范例的表达:不要假设你实际上不需要的任何东西。

在这种情况下,map实际上并不需要集合是列表,或者有序,或者可排序或任何类似的东西。唯一重要的是map能够按照没有特定顺序的方式访问集合中的所有元素。它不需要知道生成的集合是什么,它只需要知道如何构建它。因此,这就是它的类型签名所要求的。

因此,不是

map :: (a → b) → [a] → [b]

这是map的传统类型签名,现已扩展为不需要具体的List,而只需一个IterableLike数据结构

map :: (IterableLike i, IterableLike j) ⇒ (a → b) → i → j

然后进一步通过仅要求存在一个可以将结果转换为用户所需的任何数据结构的函数来进行更广义的推广。
map :: IterableLike i ⇒ (a → b) → i → ([b] → c) → c

我承认语法有点笨重,但语义是相同的。基本上,它从

开始。

def map[B](f: (A) ⇒ B): List[B]

map 的传统签名是什么?(请注意,由于 Scala 的面向对象特性,输入列表参数消失了,因为它现在是单分派 OO 系统中每个方法都具有的隐式接收器参数。)然后,它从具体的 List 泛化到更一般的 IterableLike

def map[B](f: (A) ⇒ B): IterableLike[B]

现在,它用一个函数替换了IterableLike结果集合,这个函数可以产生几乎任何东西。

def map[B, That](f: AB)(implicit bf: CanBuildFrom[Repr, B, That]): That

我真的相信这并不是很难理解。你只需要掌握几个智力工具:

  1. 你需要(大致)知道 map 是什么。如果你只给出了方法的类型签名而没有给出方法名,我承认,这会让人更难理解正在发生的事情。但是既然你已经知道 map 应该做什么,也知道它的类型签名应该是什么,你可以快速地浏览签名并关注异常之处,比如“为什么这个 map 接受两个函数作为参数,而不是一个?”
  2. 你需要能够实际上阅读类型签名。但即使你以前从未见过 Scala,这也应该很容易,因为它实际上只是你已经从其他语言中知道的类型语法的混合:VB.NET 使用方括号表示参数多态性,使用箭头表示返回类型,使用冒号分隔名称和类型,这实际上是一种规范。
  3. 你需要大致了解泛型编程的概念。(这并不是那么难理解,因为它基本上都在名称中解释得很清楚:它就是以通用方式进行编程。)

这三个问题对于任何专业的甚至是业余的程序员来说都不应该成为严重的头痛。在过去50年里,map已经成为几乎每种语言中的标准函数,不同语言具有不同的语法这一事实对于那些使用HTML和CSS设计网站的人来说应该是显而易见的,而且你无法订阅与编程相关的邮件列表,而不被一些令人讨厌的C++迷从St. Stepanov教堂解释泛型编程的优点。

是的,Scala确实复杂。是的,Scala拥有人类所知最复杂的类型系统之一,可以与Haskell、Miranda、Clean或Cyclone等语言相媲美甚至超越。但如果复杂性是反对一门编程语言成功的论据,C++早就死了,我们都会写Scheme。Scala很可能不会成功的原因有很多,但程序员在坐在键盘前之前不能费心动脑可能不会是主要原因。


36
@Jorg - 非常棒的答案,谢谢。无论您是否拥有学位,您都比我聪明。我唯一的疑虑是,我确实理解方法签名中“总体情况”的含义。然而,细节仍然令人困惑:例如,“That”是如何被推断并链接到类型“B”是一个问题,另一个问题是隐式参数从哪里来。即使没有这些详细的观察,我个人仍然觉得这是一个复杂的签名。但显然有像你这样不被这些东西吓倒的人存在! - oxbow_lakes
50
解释得很好,但你更让我相信Scala 2.8中“map”方法的签名非常复杂。 - Derek Mahar
11
像这样的语言: def map[B](f: (A) ⇒ B): IterableLike[B] 比像这样的语言更容易理解: def map[B, That](f: A ⇒ B)(implicit bf: CanBuildFrom[Repr, B, That]): That 请注意,翻译并不改变原文的含义和信息,而只是将其转化为更通俗易懂的形式。 - Mark Essel
216
我觉得很有趣的是,你一开始声称只懂得基本的 Ruby 和 Smalltalk,并且没有相关学术背景...但后来又自称了解像 Miranda 和 Clean 这样语言中类型系统的复杂性;这些语言通常只为程序员极客和学者所知。 - Sami
14
你说得有道理,与Haskell的比较不正确之处在于“map :: (a -> b) -> [a] -> [b]”仅适用于列表。然而,Functor类的通用版本仍然比Scala的版本简单得多: class Functor f where fmap :: (a -> b) -> f a -> f b - Orclev
显示剩余10条评论

175

C++ 中的相同之处:

template <template <class, class> class C,
          class T,
          class A,
          class T_return,
          class T_arg
              >
C<T_return, typename A::rebind<T_return>::other>
map(C<T, A> &c,T_return(*func)(T_arg) )
{
    C<T_return, typename A::rebind<T_return>::other> res;
    for ( C<T,A>::iterator it=c.begin() ; it != c.end(); it++ ){
        res.push_back(func(*it));
    }
    return res;
}

105
他们说Scala很晦涩难懂。当然啦! - missingfaktor
24
想象一下,如果使用适当的自我描述标识符而不是任意的大写字母,会是什么样子。 :-) - Ti Strga
14
比较这两者是有用的,但如果把实施方式省略掉会更加公平。 - Aaron Novstrup
2
我不是强制使用函数指针的忠实粉丝。显然,func 的类型应该是一个模板参数,并且您应该使用 result_ofis_callable 来获取其他类型并适当地约束重载集 :-) - Kerrek SB
2
我的眼睛很痛!!! - Ashkan Kh. Nazary

71

我理解你的苦恼,但是说实话像你我这样的人,或者几乎任何常规的Stack Overflow用户都不是主流。

我的意思是...大多数程序员不会关心那种类型签名,因为他们永远不会看到它们!他们不会阅读文档。

只要他们看到了一些代码如何工作的示例,而且代码在生成他们期望的结果时没有失败,他们就永远不会查看文档。当代码失败时,他们会查看文档,并期望在顶部看到使用示例。

考虑到这些问题,我认为:

  1. 任何(大多数)遇到那种类型签名的人,如果他们对Scala持先入为主偏见,就会嘲笑Scala;如果他们喜欢Scala,就会认为它是Scala强大的象征。

  2. 如果文档没有增加使用示例并清楚地解释方法的用途和如何使用它,它可能会削弱Scala的采用率。

  3. 从长远来看,这不重要。Scala能够做到这一点将使为Scala编写的库更加强大和更安全。这些库和框架将吸引喜欢强大工具的程序员。

  4. 喜欢简单和直接的程序员将继续使用PHP或类似的语言。

遗憾的是,Java程序员非常喜欢强大的工具,因此在回答这个问题时,我刚刚修正了对主流Scala采用的期望。我毫不怀疑Scala将成为一门主流语言。虽然不是C级别主流,但也可能是Perl或PHP级别的主流。

说到Java,你是否曾经替换过类加载器?你是否曾经研究过其中涉及的内容?如果你看看框架编写者的地方,Java会让人感到害怕。只是大多数人不会注意到这些细节。据我看来,同样的事情也适用于Scala,但是早期的采用者有一个倾向,就是遇到任何难题时都会去看看,是否有什么隐藏的东西。


10
只要他们看到代码的工作示例,并且代码在产生他们所期望的结果时没有出错,他们就不会再去查看文档。当这种情况不再奏效时,他们会查看文档,并期望文档中有使用示例。悲伤而真实。 - gamliela
9
@gamliela,我认为我们不应该对此感到悲伤。知识总是有多个层次可以应用的,并且在任何系统中,他人的工作和信任(经过同行评审)总是可以利用的,就像我们每天使用算术一样,完全无视其背后运作的可怕代数学。 - lcn

55

这会让人们不喜欢使用Scala吗?

是的,但它也将防止有人被吓到。自从Scala支持高类别类型以来,我一直认为缺乏使用高类别类型的集合是一个主要弱点。虽然它会使API文档更加复杂,但确实使使用更加自然。

这会让Scala在商业世界中声名狼藉,成为只有专门的博士生才能理解的学术玩具吗?CTO和软件主管会因此害怕吗?

一些人可能会。我认为Scala对于许多“专业”开发人员来说并不容易理解,部分原因是Scala的复杂性,部分原因是许多开发人员不愿意学习。那些雇用这些开发人员的CTOs将因此而担心。

库的重新设计是个明智的想法吗?

绝对是。即使它仍有一些粗糙的边缘,它使集合与语言和类型系统更好地结合起来。

如果你在商业上使用Scala,你会担心这个吗?你计划立即采用2.8还是等待看看会发生什么?

我没有在商业上使用它。我可能会等到2.8.x系列至少经过几次修订后再尝试引入它,以便能够排除错误。我也会等待看EPFL在改进其开发和发布流程方面取得多大的成功。我看到的情况很有希望,但我工作的公司比较保守。

关于“Scala是否过于复杂,不适合主流开发人员”的更一般的话题...

大多数开发人员,无论是主流还是其他,都在维护或扩展现有系统。这意味着他们使用的大部分内容是很久以前做出的决策。仍然有很多人在编写COBOL。

明天的主流开发人员将维护和扩展正在建造的应用程序。其中许多应用程序并不是由主流开发人员建造的。明天的主流开发人员将使用今天最成功的新应用程序开发人员所使用的语言。


31
"它还将防止人们被吓到。这是绝对的。Scala是第一种使得类似Haskell(在其类型系统的能力方面)工程化成为我们许多人可能的语言。我没有办法说服我的工作使用Haskell,但Scala确实有机会,因此我喜欢它,并且(当我认为有意义时)将尝试在工作中采用它,或者至少得到接受。" - andrew cooke
我也点赞。鉴于Scala更注重语言深度和严谨性而非大众可接受性的前提,这些答案非常合适。 - Carl Smotricz
16
明天的主流开发者将使用当今最成功的新应用程序开发者使用的语言。+1.说得好。 - Vasil Remeniuk

46
Scala社区可以通过注重实践和示范教学来帮助缓解对Scala新手程序员的恐惧,提供很多由小到大逐步增长的示例。以下是几个采用这种方法的网站: 在这些网站上花一些时间,就会很快意识到Scala及其库虽然可能难以设计和实现,但在常见情况下使用并不那么困难。

43

我在一所廉价的“大众市场”的美国大学获得了本科学位,因此我认为我处于用户智力(或至少教育)水平的中间 :) 我只是尝试了几个月Scala,并且已经开发了两到三个非平凡的应用。

特别是现在IntelliJ发布了他们出色的IDE,其中我认为目前最好的Scala插件,Scala开发相对无痛:

  • 我发现我可以将Scala当作“不带分号的Java”,也就是说,我编写的代码与我在Java中所做的类似,并从类型推断等语法的简洁性中受益。当我处理异常的时候,更加方便。类定义没有getter/setter样板文件,更加简洁。

  • 偶尔我会成功地写出一行代码来完成Java的多行等效代码。适用时,像map、fold、collect、filter等函数式方法链非常有趣和优雅。

  • 我很少发现自己从Scala的高级功能中受益:闭包和部分(或柯里化)函数,模式匹配......那种事情。

作为一名新手,我继续努力理解简洁和惯用的语法。没有参数的方法调用不需要括号,除非必需;在匹配语句中,案例需要一个fat箭头(=〉),但也有需要thin箭头(-〉)的地方。许多方法具有简短但相当难懂的名称,如/:或\: - 如果我翻阅足够的手册页,我可以完成我的工作,但我的一些代码最终看起来像Perl或行噪声。具有讽刺意味的是,最流行的语法缩写之一却不见了:我一直被“Int”没有定义“++”方法这一事实所困扰。

这只是我的观点:我认为Scala具有C++的能力和C++的复杂性和可读性。语法复杂度也使得API文档难以阅读。

Scala是一个在很多方面都非常 周密思考 和出色的语言。我怀疑许多学者都会喜欢用它来编程。然而,它也充满了巧妙之处和需要注意的地方,相比Java来说具有更高的学习曲线和更难以理解。如果我浏览论坛,并看到有多少开发人员仍然在苦苦挣扎于Java的一些细节问题上,我无法想象Scala会成为主流语言。没有公司能够证明将开发人员派去参加为期3周的Scala课程比原先只需要1周的Java课程更加合理。


9
很抱歉有那么多评论。对于任何语言来说,一周的时间几乎是不够的,但这并不能阻止经理们将这个时间限制实施。我曾经被要求用3天时间将一组C++开发人员紧急转换成Java。我请求5天时间,但由于预算原因被缩短了。 - Carl Smotricz
22
我的第一份工作,在面试结束时,给了我一本 C++ 书让我在周一开始工作之前先学习。你们都太软弱了。 - Tom Hawtin - tackline
12
你们两个孩子真是太幸福了。当时我在面试的时候被给予了电脑电路图(那时还没有CPU),然后被告知有两个小时时间来解决一个问题。请注意,这是在面试过程中进行的。 - Daniel C. Sobral
33
我曾被要求在面试中使用0和1来解决背包问题,并在线性时间内完成。我尝试了一下,但不幸的是我只有时间创建了Eclipse(我怀疑它可以归约为背包问题)。#tall_tale - Alex Miller
10
@Alex 这表明缺乏想象力。在左边放一个大零,右边放两个小零:上下排列,上面的略微向左。将那个1放在这两个小零之间,从左下到右上延伸。说这就是在线性时间内解决背包问题的机会。完成了。 :-) 赞同把Eclipse和Knapsack等同起来,加1分。 - Daniel C. Sobral
显示剩余10条评论

33
我认为这种方式的主要问题在于 (implicit bf : CanBuildFrom[Repr, B, That]) 没有任何解释。尽管我知道隐式参数是什么,但没有任何说明表明它如何影响调用。在scaladoc中查找只会让我更加困惑(与CanBuildFrom相关的类甚至没有文档)。
我认为一个简单的 "必须在作用域中存在提供类型为 B 的对象构建器到返回类型 That的隐式对象 bf" 的解释可能会有所帮助,但这似乎是一个复杂的概念,当你只想将 A 映射到 B 时。实际上,我不确定这是否正确,因为我不知道类型 Repr 意味着什么,而且 Traversable 的文档确实没有任何提示。
因此,我只剩下两个选项,两者都不令人愉快:
  • 假设它会像旧的 map 函数和大多数其他语言中的 map 函数一样工作
  • 深入研究源代码
我理解 Scala 本质上是暴露了这些东西的内部工作原理,最终提供了一种方法来实现 oxbow_lakes 描述的功能。但是这会让函数签名变得复杂。

2
Repr是可遍历的表示,例如ListSetMap。我认为,作为框架,如果你要开始查看方法签名(而不仅仅是通过复制示例来使用方法),你必须先了解通用设计。在我看来,Scaladoc应该充满示例用法。 - oxbow_lakes
10
那么,我如何确定Repr的含义呢?我期望在scaladoc中有解释,但对我来说并不明显。我认为这是scaladoc中常见的一个模式(看看Actor.reactActor.receive - 我被告知它们完全执行不同的操作,但它们的scaladoc却是相同的)。 - davetron5000
7
我赞同 davetron5000 的观点。我对 Scala 很熟悉,但隐式定义仍然让我感到头痛。原因不是因为 implicit 定义本身,而是它们的使用方式。肯定应该有更好的文档和工具支持来理解 Scala 类型。话虽如此,我认为类型系统确实有重要的东西可以提供。但我们还只是在合理编程的旅途开始阶段。 - egaga

22

我是Scala的初学者,对于这个类型签名,我并没有看出什么问题。其中,参数是要映射的函数,而隐式参数则是返回正确集合的构建器。清晰易懂。

整个过程非常优雅。构建器类型参数让编译器选择正确的返回类型,而隐式参数机制则将这个额外的参数隐藏在类用户之外。我尝试了下面的代码:

Map(1 -> "a", 2 -> "b").map((t) => (t._2) -> (t._1)) // returns Map("a" -> 1, "b" -> 2)
Map(1 -> "a", 2 -> "b").map((t) =>  t._2)            // returns List("a", "b")

这就是正确实现的多态性。

当然,这不是一种主流范式,会吓跑很多人。但是,它也会吸引很多重视其表现力和优雅性的人。


20
很遗憾,您提供的map签名是错误的,并且确实存在合理的批评。
首要的批评是通过破坏map的签名,我们得到了更一般的东西。普遍的错误是认为这是一种优点,但它并不是。map函数非常明确定义为协变函子Fx -> (x -> y) -> Fy,遵循组合和恒等两个定律。任何归属于“map”的其他东西都是一种讽刺。
给出的签名是另外一种东西,但它不是map。我怀疑它试图成为一种专门的、稍微改进的“遍历”签名,来自论文《迭代器模式的本质》。以下是它的签名:
traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)

我将把它转换成Scala:

def traverse[A, B](f: A => F[B], a: T[A])(implicit t: Traversable[T], ap: Applicative[F]): F[T[B]

当然会失败——它不够通用!此外,它略有不同(请注意,您可以通过在Identity functor上运行traverse来获取map)。但是,我怀疑如果库的编写者更加了解文档良好的库概述("Applicative Programming with Effects"早于前面提到的内容),那么我们就不会看到这种错误。
其次,map函数是Scala中的特例,因为它在for-comprehensions中使用。不幸的是,这意味着一个更好地装备的库设计人员不能忽视这个错误,而不牺牲综合理解的语法糖。换句话说,如果Scala库设计者要摧毁一个方法,那么这很容易被忽略,但请勿破坏map!
我希望有人能就此发表意见,因为目前这样做将变得更加困难,而Scala似乎出于强烈反对我的原因而坚持制造错误。也就是说,“普通程序员的不负责任的反对意见(即太难!)”的解决方案不应该是“让他们满意,以使之变得更容易”,而应该是提供指针和帮助,帮助他们成为更好的程序员。在这个问题上,我和Scala的目标存在争执,但回到您的观点。
您可能在阐述您的观点,预测“普通程序员”的具体回应。也就是说,那些声称“但这太复杂了!”或类似言论的人。这些是您提到的Yegges或Blochs。我对反知识分子/实用主义运动的这些人的回应相当严厉,我已经预料到了一连串的回应,因此我将不再赘述。
我真心希望Scala库能得到改善,或者至少可以将错误安全地隐藏起来。Java是一种语言,其中“试图做任何有用的事情”是如此令人难以置信的昂贵,以至于由于无法避免的大量错误,它通常不值得去尝试。我恳求Scala不要走上同样的道路。

3
嗨,托尼 - 感谢你在这里提供的思考性意见。我有两个回应。首先,我没有提到“普通程序员”,也不认为Scala一定针对他们。不管是不是自大,我相信我是超过平均水平的;然而,我仍然觉得类型签名很困难!换句话说,我仍然担心,对于上过平均水平的程序员——Scala的目标市场,可能会被吓跑。 - oxbow_lakes
6
第二点是我根本不同意你对Scala的认识:Scala是一种实用主义语言,而不是理论上纯粹的语言。否则为什么要在JVM之上设计它?这是一个纯粹的实用决策——它旨在面向"现实世界"中的开发人员——这个选择可能需要妥协!还要注意的是,Bloch和Yegge远非普通程序员——但这正是我的观点。即使是备受尊敬和聪明的人也可能对复杂性和纯洁性有与你不同的看法。不幸的是,他们也具有极高的影响力。 - oxbow_lakes
3
你好,oxbow_lakes, Scala的一个明确目标是迎合一般程序员,即使这样做会牺牲准确性和实用性。有些高水平的程序员被排斥了(我有几个例子),但不是因为类型签名令人生畏,而是因为某些错误的本质。我并没有说Scala是实用的还是理论的。此外,我甚至不认同(常见的?)存在这种二分法的想法。Scala库已经搞砸了map的签名。我已经在解决Scala的错误问题多年了,特别是关于库的问题。现在又得再次处理这个问题。 - Tony Morris
5
我不认为Bloch或Yegge备受尊敬或聪明,但他们确实具有相当大的影响力。是的,这很不幸。 - Tony Morris
9
为什么你将traverse与Scala的扩展签名联系起来?对于单子函子,Scala的map是标准的fmap。但是BitSet和Map [A,B]都不是单子函子,但map在它们上面有一个有意义的定义。这就是Scala签名的动机,而traverse并不能解决这个问题。为什么广泛性是一件坏事?应用函子跟踪效果,在Scala中它们的目的是什么?最后,我认为Scala的通用映射可以通过一个通用遍历的实现来实现,它接受一个CanBuildFrom并返回一个可能不同的可遍历对象:无需牺牲理解力! - Blaisorblade

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接