我刚刚开始研究即将发布的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,你会担心吗?您是否计划立即采用2.8版本,还是等待结果?

Steve Yegge曾攻击Scala(在我看来是错误的),因为他认为Scala的类型系统过于复杂。我担心有人会用这个API来传播FUD(类似于Josh Bloch如何吓得JCP不敢向Java添加闭包)。

注意-我应该清楚,虽然我认为约书亚·布洛赫在拒绝BGGA关闭提案方面有影响力,但我不认为这是因为他诚实地认为该提案代表了错误。


尽管我的妻子和同事一直在告诉我,我并不认为自己是个白痴:我在牛津大学获得了很好的数学学位,我已经在商业编程近12年,在Scala编程大约一年(也是商业编程)。

请注意,煽动性主题标题引用了20世纪80年代初英国一个政党的宣言。这个问题是主观的,但这是一个真实的问题,我已经把它改成了CW,我想就此事发表一些意见。


当前回答

嗯,我可以理解你的痛苦,但坦率地说,像你和我这样的人——或者几乎任何一个普通的StackOverflow用户——都不是规则。

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

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

考虑到这些,我认为:

任何人(像大多数人一样)遇到这种类型签名时,如果事先对Scala进行了处理,就会对其进行无限的嘲笑,如果他们喜欢Scala,就会认为它是Scala力量的象征。如果文档没有得到增强以提供使用示例,并清楚地解释方法的用途和使用方法,那么可能会有点影响Scala的采用。从长远来看,这无关紧要。Scala可以做这样的事情,这将使为Scala编写的库更加强大,使用起来更加安全。这些库和框架将吸引熟悉强大工具的程序员。喜欢简单和直接的程序员将继续使用PHP或类似的语言。

唉,Java程序员非常热衷于强大的工具,所以,在回答这个问题时,我刚刚修正了我对主流Scala采用的期望。我毫不怀疑Scala会成为主流语言。不是C主流,但可能是Perl主流或PHP主流。

说到Java,您曾经替换过类加载器吗?你有没有调查过这涉及到什么?如果你看看框架编写者所做的事情,Java可能会很可怕。只是大多数人都不知道。这同样适用于Scala,IMHO,但早期采用者倾向于在他们遇到的每一块岩石下寻找,看看是否有什么隐藏在那里。

其他回答

我没有博士学位,也没有任何其他学位,既没有计算机科学,也没有数学,也没有其他领域。我以前没有使用Scala或任何其他类似语言的经验。我甚至没有使用远程可比类型系统的经验。事实上,我所掌握的不仅仅是表面知识,甚至还有一种类型系统的语言是Pascal,它并不完全以其复杂的类型系统而闻名。(虽然它确实有范围类型,而AFAIK几乎没有其他语言有,但这在这里并不重要。)我知道的其他三种语言是BASIC、Smalltalk和Ruby,它们都没有类型系统。

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

在这种情况下,map实际上不需要集合是列表,也不需要被排序或可排序等。地图唯一关心的是,它可以一个接一个地访问集合的所有元素,但没有特定的顺序。它不需要知道生成的集合是什么,只需要知道如何构建它。因此,这就是它的类型签名所要求的。

所以

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

这是映射的传统类型签名,它一般不需要具体的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]

这是地图的传统签名。(注意,由于Scala的面向对象特性,输入列表参数是如何消失的,因为它现在是单个分派OO系统中每个方法都具有的隐式接收器参数。)

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

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

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

我真的相信这并不难理解。你只需要几个智能工具:

你需要知道(大致)map是什么。我承认,如果你只给出了类型签名,而没有给出方法的名称,那么很难弄清楚到底发生了什么。但既然你已经知道map应该做什么,而且你知道它的类型签名应该是什么,你就可以快速扫描签名并关注异常情况,比如“为什么这个映射将两个函数作为参数,而不是一个?”您需要能够实际读取类型签名。但是,即使您以前从未见过Scala,这也应该很容易,因为它实际上是您从其他语言中已经知道的类型语法的混合:VB.NET使用方括号来表示参数多态性,使用箭头表示返回类型,使用冒号来分隔名称和类型,这实际上是一种规范。您需要大致了解泛型编程是什么。(这并不难理解,因为它基本上都是用名称拼写的:它实际上只是以通用方式编程)。

这三项都不应该让任何专业程序员甚至业余程序员头疼。在过去的50年中,map已经成为几乎每一种语言的标准功能,不同的语言都有不同的语法,这一事实对于任何使用HTML和CSS设计网站的人来说都是显而易见的,如果没有一些来自圣教堂的讨厌的C++粉丝,你就无法订阅甚至远程编程相关的邮件列表。Stepanov解释泛型编程的优点。

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

我根本不懂Scala,但几周前我还不会读Clojure。现在我能读到大部分,但除了最简单的例子之外,我还写不出任何东西。我想Scala也没什么不同。你需要一本好书或一门课程,这取决于你的学习方式。只是看了上面的地图声明,我大概得到了其中的1/3。

我认为,更大的问题不在于这些语言的语法,而在于采用和内化使它们在日常生产代码中可用的范例。对我来说,Java并不是C++的巨大飞跃,也不是C的巨大飞跃。Pascal和Basic等根本不是Java的飞跃。但用Clojure这样的函数式语言编写代码是一个巨大的飞跃(对我来说)。我想在Scala中,你可以用Java风格或Scala风格编写代码。但在Clojure中,您将创建一个相当混乱的环境,试图让您的命令习惯远离Java。

我拥有一所廉价的“大众市场”美国大学的本科学位,所以我可以说我处于用户智能(或至少是教育)的中等水平:)我涉足Scala仅几个月,并开发了两三个非平凡的应用程序。

特别是现在IntelliJ发布了他们的优秀IDE,IMHO是目前最好的Scala插件,Scala开发相对轻松:

我发现我可以使用Scala作为一个“没有分号的Java”,也就是说,我编写的代码与我在Java中所做的代码相似,并且从语法简洁(比如通过类型推断获得的简洁)中获益匪浅。异常处理,当我做的时候,更方便。没有getter/setter样板,类定义就不那么冗长了。偶尔我会写一行代码,以实现相当于多行Java代码的功能。在适用的情况下,诸如map、fold、collect、filter等功能方法链的组成既有趣又美观。我很少能从Scala更强大的功能中获益:闭包和部分(或curried)函数、模式匹配。。。那种事。

作为一个新手,我继续努力学习简洁和惯用的语法。没有参数的方法调用不需要括号,除非它们需要括号;match语句中的case需要一个粗箭头(=>),但也有一些地方需要一个细箭头(->)。许多方法都有简短但相当隐晦的名称,如/:或\:-如果我翻了足够多的手册页,我可以完成我的工作,但我的一些代码看起来像Perl或行噪声。具有讽刺意味的是,最流行的语法速记之一在实际操作中缺失了:我一直被Int没有定义++方法这一事实所困扰。

这只是我的看法:我觉得Scala具有C++的强大功能,同时又具有C++的复杂性和可读性。语言的语法复杂性也使得API文档难以阅读。

Scala在很多方面都是经过深思熟虑的。我想很多学者都会喜欢用它编程。然而,它也充满了智慧和陷阱,它比Java有更高的学习曲线,更难阅读。如果我浏览论坛,看到有多少开发人员仍在为Java的精细之处而挣扎,我无法想象Scala会成为主流语言。没有一家公司能够证明派遣开发人员参加为期3周的Scala课程是合理的,因为他们以前只需要一周的Java课程。

我认为该方法的主要问题是(隐式bf:CanBuildFrom[Rrep,B,that])没有任何解释。尽管我知道什么是隐式参数,但没有任何信息表明这会如何影响调用。浏览scaladoc只会让我更加困惑(很少有与CanBuildFrom相关的类甚至有文档)。

我认为一个简单的“bf的作用域中必须有一个隐式对象,它为B类型的对象提供了一个构建器”会有所帮助,但当您真正想做的是将a映射到B时,这是一个令人兴奋的概念。事实上,我不确定这是对的,因为我不知道Repr类型是什么意思,而Traversable的文档也毫无头绪。

所以,我有两个选择,都不令人满意:

假设它只适用于旧地图的工作方式以及地图在大多数其他语言中的工作方式深入了解源代码

我认为Scala本质上是在揭示这些东西是如何工作的,最终这是提供了一种实现oxbow_lakes所描述的方法。但签名会分散注意力。

我是一个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")

多态性做得很好。

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