斯卡拉2.8collections图书馆是“历史上最长的遗书”吗?

我刚开始着眼于即将发布的2.8版本中的Scala集合库重新实现 。 熟悉2.7版库的人会注意到,从使用的angular度来看,图书馆的变化不大。 例如…

> 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 

对于这样简单的function,这是一个令人生畏的签名,我发现自己正在努力去理解。 我不认为Scala有可能成为下一个Java (或者C / C ++ / C#) – 我不认为它的创build者正在瞄准这个市场 – 但是我认为Scala是可行的下一个Ruby或Python(即获得重要的商业用户群)

  • 这是否会让人们到斯卡拉?
  • 这是否会给斯卡拉在商业世界作为一个学术玩具 ,只有专门的博士生才能理解一个坏名字? 首席技术官和软件负责人会被吓跑吗?
  • 图书馆是否重新devise了一个明智的主意?
  • 如果您在商业上使用Scala,您是否担心这一点? 你打算立即采用2.8还是等待看看会发生什么?

史蒂夫·叶格 ( Steve Yegge) 曾经 (我认为)错误地将斯卡拉 ( Scala )视为过度复杂的types系统。 我担心有人会用这个API传播FUD (类似于Josh Bloch害怕JCPencryption闭包给Java)。

我应该清楚的是,尽pipe我相信Joshua Bloch在拒绝BGGAclosures提案方面有影响力,但我并不认为这是他诚实的信念,认为这个提案是错误的。


尽pipe我的妻子和同事总是告诉我,但我不认为自己是白痴:我在牛津大学获得了很好的math学位,而且我已经在商业上进行了近12年的商业编程,一年(也是商业上)。

请注意,煽动主体标题是关于英国政党在20世纪80年代初宣言的引文 。 这个问题是主观的,但这是一个真正的问题,我已经做了CW,我想就此事提出一些意见。

我希望这不是“遗书”,但我可以看到你的观点。 您同时触及Scala的实力和问题:它的可扩展性 。 这使我们可以在库中实现最主要的function。 在其他一些语言中,像mapcollect类的序列将被内置,没有人必须看到编译器必须经过的所有环节才能使其顺利运行。 在斯卡拉,这一切都在图书馆,因此公开。

事实上,由其复杂types支持的mapfunction相当先进。 考虑这个:

 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!) 

看看你总是得到最好的types? 如果将Int映射到Int则会再次得到一个BitSet ,但是如果将Int映射到String ,则会得到一个通用的Set 。 map的结果的静态types和运行时表示都依赖于传递给它的函数的结果types。 即使设置为空,这也可以工作,所以这个function永远不会被应用! 据我所知,没有其他具有相同function的集合框架。 然而从用户的angular度来看,这是事情应该如何运作的。

我们所面临的问题是,所有使这种事情发生的巧妙技术都会泄露到变得庞大而可怕的types签名中。 但是,也许用户不应该默认显示map的完整types签名? 如果她在BitSet查看map ,她会得到:

 map(f: Int => Int): BitSet (click here for more general type) 

文档不会在这种情况下,因为从用户的angular度来看,地图的types是(Int => Int) => BitSet 。 但map也有一个更一般的types,可以通过点击另一个链接进行检查。

我们还没有在我们的工具中实现这样的function。 但我相信我们需要这样做,以避免吓跑人们,并提供更有用的信息。 有了这样的工具,希望聪明的框架和图书馆不会成为自杀票据。

我没有博士学位,也没有任何其他types的学位,无论是在math,math,甚至任何其他领域。 我没有Scala的经验,也没有其他类似的语言。 即使是远程可比types的系统,我也没有经验。 事实上,我所拥有的仅仅是肤浅的知识,甚至一种types系统的语言只是帕斯卡,而不是以其复杂的types系统而闻名。 (尽pipe它的确有范围types,但是其他语言几乎没有其他语言,但在这里并不真正相关)。我所知道的其他三种语言是BASIC,Smalltalk和Ruby,它们都没有types系统。

然而,我完全不了解你发布的mapfunction的签名。 在我看来,它和我见过的其他所有语言都具有相同的签名。 不同的是,这个版本更通用。 它看起来更像是一个C ++ STL的东西,比如Haskell。 特别是,它只需要参数是IterableLike就抽象出具体的集合types,并且只需要一个隐式的转换函数就可以从结果集合中构build出一些东西 ,从而抽象出具体的返回types。 是的,这是相当复杂的,但它实际上只是generics编程一般范式的expression:不要假设任何你不需要的东西。

在这种情况下, map实际上并不需要集合成为一个列表,或者被sorting或者是可sorting或者类似的东西。 map唯一关心的是它可以一个接一个地访问集合中的所有元素,但是没有特定的顺序。 而且不需要知道所得到的集合是什么,只需要知道如何构build它。 所以,这就是它的types签名所要求的。

所以,而不是

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

这是传统的maptypes签名,它被推广到不需要一个具体的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的面向对象特性,input列表参数消失了,因为它现在是单分派面向对象系统中每个方法所具有的隐式接收器参数。)然后,它从一个具体List推广到更多一般IterableLike

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

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

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

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

  1. 你需要知道(大致)什么map 。 如果你给出了没有方法名称的types签名,我承认,要弄清楚发生了什么会更困难。 但既然你已经知道 map应该做什么,而且你知道它的types签名应该是什么样的,你可以快速扫描签名并专注于exception,比如“为什么这个map有两个函数作为参数,而不是一个?”
  2. 您需要能够实际读取types签名。 但是,即使你以前从未见过Scala,这也应该是相当容易的,因为它实际上只是你从其他语言中已经知道的types语法的混合体:VB.NET使用方括号表示参数多态性,并用箭头表示返回types和冒号来分隔名称和types,实际上是常态。
  3. 你大致需要知道什么是generics编程。 (这不是很难弄清楚,因为它的名字基本上都是拼写出来的:它实际上只是以通用的方式进行编程)。

这三者都不应该给任何专业甚至业余爱好者程序员带来严重的头痛。 map在过去的50年中几乎每一种语言都是标准的function,不同的语言有不同的语法的事实对任何devise了HTML和CSS的网站的人来说都是显而易见的,而且你不能订阅甚至远程编程相关的邮件列表,没有一些恼人的C + +从圣斯捷潘诺夫教会解释generics编程的优点。

是的,斯卡拉复杂的。 是的,斯卡拉拥有人类已知的最复杂的types系统之一,与Haskell,Miranda,Clean或Cyclone等语言相媲美,甚至超越语言。 但是,如果复杂性是一个反对编程语言成功的论点,那么C ++早就死了,我们都会写Scheme。 Scala很可能不会成功的原因有很多,但程序员在坐在键盘前不能打开脑子的事实可能不会成为主要原因。

同样的事情在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; } 

那么,我可以理解你的痛苦,但坦率地说,像你我这样的人,或者几乎任何常规的Stack Overflow用户都不是这个规则。

我的意思是说…大多数程序员不会关心这种types的签名,因为他们永远不会看到它们 ! 他们不读文件。

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

考虑到这些,我认为:

  1. 任何遇到过这种types签名的人(如大多数人)如果对Scala进行预先处理,就会嘲笑Scala,如果他们喜欢Scala,将会认为它是Scala的力量的象征。

  2. 如果不提供文档来提供使用示例并清楚地说明一个方法是什么以及如何使用它,它可能会减lessScala的采用。

  3. 从长远来看,这并不重要。 Scala 可以做这样的事情,这将使Scala编写的库更加强大和更安全。 这些库和框架将吸引程序员强大的工具。

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

唉,Java程序员对电动工具有很大的了解,所以在回答这个问题之前,我刚刚修改了我对Scala主stream应用的期望。 我毫不怀疑斯卡拉将成为主stream语言。 不是C主stream,但也许Perl主stream或PHP主stream。

谈到Java,你有没有更换类加载器? 你有没有看过涉及到什么? Java可以是可怕的,如果你看看框架作家做的地方。 只是大多数人没有。 恕我直言,同样的事情适用于斯卡拉,但是早期的采用者往往会看到他们遇到的每一块岩石,看看是否有东西藏在那里。

这是否会让人们到斯卡拉?

是的,但也会阻止人们被推迟。 我认为自从Scala获得对更高级别types的支持以来,缺乏使用更高types的集合是主要的弱点。 它使API文档更复杂,但它确实使用起来更自然。

这是否会给scala在商业世界中一个坏名字作为一个学术玩具,只有专门的博士生才能理解? 首席技术官和软件负责人会被吓跑吗?

有些可能会。 我不认为Scala可以被许多“专业”开发人员访问,部分原因是Scala的复杂性,部分原因是许多开发人员不愿意学习。 雇佣这样的开发商的首席技术官会被吓倒。

图书馆是否重新devise了一个明智的主意?

绝对。 它使得集合与其余的语言和types系统更好地契合,即使它仍然有一些粗糙的边缘。

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

我没有使用它商业。 我甚至可能会等到至less有一些人转向2.8.x系列之后,才试图介绍它,以便能够清除bug。 我还要等一等,看看EPFL在改进发行过程中取得了多大的成功。 我所看到的看起来充满希望,但我为一家保守的公司工作。

一个更普遍的话题是“Scala对于主stream开发者来说太复杂了”?

大多数开发者,主stream或其他,正在维护或扩展现有的系统。 这意味着他们所使用的大部分内容都是由很久以前的决定所决定的。 还有很多人在写COBOL。

明天的主stream开发人员将维护和扩展当前正在构build的应用程序。 许多这些应用程序不是由主stream开发人员构build的。 明天的主stream开发人员将使用当今最成功的新应用程序开发人员正在使用的语言。

Scala社区可以帮助减轻Scala新手程序员的恐惧的一种方式是专注于实践,并以身作则 – 很多从小开始并逐渐扩大的例子。 以下是一些采用这种方法的网站:

  • 每日斯卡拉
  • 在小小的时候学习Scala
  • 简单的斯卡拉

在这些网站上花费了一些时间之后,人们很快意识到Scala及其库可能难以devise和实现,特别是在常见的情况下,使用起来并不困难。

我从美国一所便宜的“大众市场”获得了本科学位,所以我会说我落入了用户智能(或至less是教育)规模的中间:)我一直在Scala涉足几个月并已经在两个或三个非平凡的应用程序工作。

特别是现在,IntelliJ已经发布了他们的精美的IDE与什么恕我直言,目前是最好的Scala插件,斯卡拉发展是相对无痛的:

  • 我发现我可以使用Scala作为“没有分号的Java”,也就是说,我编写了类似于Java的代码,并且从types推理中获得的语​​法简洁中受益匪浅。 当我做这件事时,exception处理更方便。 如果没有getter / setter样板文件,类定义就不那么冗长了。

  • 有一段时间,我设法写一行来完成相当于多行的Java。 在适用的情况下,如地图,折叠,收集,filter等function方法链是有趣的构成和优雅的看。

  • 我很less从Scala的高性能function中受益:closures和部分(或者咖喱)function,模式匹配…这种事情。

作为一个新手,我继续与简洁和惯用的语法进行斗争。 不带参数的方法调用不需要括号,除非他们在做什么; 匹配语句中的例子需要一个胖箭头( => ),但也有一些地方需要细箭头( -> )。 许多方法都有简短但相当神秘的名字,如/:\: – 如果我翻动了足够多的手册页,我可以完成我的工作,但是我的一些代码最终看起来像Perl或线路噪声。 具有讽刺意味的是,语法简写中最stream行的部分之一就是缺less动作:我不断地被Int没有定义一个++方法的事实所Int

这只是我的看法:我觉得Scala具有C ++的强大function,结合C ++的复杂性和可读性。 该语言的语法复杂性也使API文档难以阅读。

斯卡拉在很多方面都是非常深思熟虑的 。 我怀疑很多学者会喜欢编程。 但是,它也充满了聪明和困惑,它比Java有更高的学习曲线,而且更难读。 如果我仔细阅读论坛,看看有多less开发者仍然在为Java的优点苦苦挣扎,那么我不能想象Scala会成为主stream语言 。 没有任何公司可以在3星期的Scala课程上为其开发人员提供理由,因为以前他们只需要一个星期的Java课程。

我认为这个方法的主要问题是(implicit bf : CanBuildFrom[Repr, B, That])没有任何解释。 即使我知道什么隐含的参数是没有任何指示如何影响调用。 追逐scaladoc只会让我更加困惑(很less有与CanBuildFrom相关的类甚至有文档)。

我认为一个简单的“在范围内必须有一个隐含的对象,为Btypes的对象提供了一个build造者返回types” That将有所帮助,但是当你真正想要做的事情是一个令人头疼的概念时, AB的。 事实上,我不确定这是否正确,因为我不知道Repr是什么types,而Traversable的文档根本就没有提供任何线索。

所以,我剩下两个select,他们都不愉快:

  • 假设它只会工作在旧地图的工作方式以及地图如何在大多数其他语言中工作
  • 深入挖掘源代码

我明白,Scala本质上揭示了这些事情如何工作的胆量,并最终提供了一种方法来实现oxbow_lakes所描述的。 但是签名让人分心

我是一个斯卡拉初学者,我真的没有看到这种types签名的问题。 该参数是映射的函数,隐式参数是构build器返回正确的集合。 清晰可读。

实际上整个事情都很优雅。 构build器types参数让编译器select正确的返回types,而隐式参数机制隐藏类用户的这个额外参数。 我试过这个:

 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") 

这是正确的多态性。

现在,理所当然,这不是一个主stream的范例,它会吓跑许多人。 但是,它也将吸引许多重视其performance力和高雅的人。

不幸的是,你给的地图签名对于地图来说是不正确的,确实有合法的批评。

第一个批评是,通过颠覆地图签名,我们有更一般的东西。 相信这是默认的优点是一个常见的错误。 事实并非如此。 映射函数被很好地定义为一个协变函子Fx – >(x – > y) – > Fy,并遵守组合和身份的两个定律。 任何其他归因于“地图”是一个滑稽。

给定的签名是别的东西,但它不是地图。 我猜想它是试图成为一个专门的,稍微改变的版本的“遍历”签名从论文,迭代器模式的本质。 这是它的签名:

 traverse :: (Traversable t, Applicative f) => (a -> fb) -> ta -> f (tb) 

我将把它转换成Scala:

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

当然,它失败了 – 这不够普遍! 此外,它略有不同(请注意,您可以通过运行遍历身份仿函数来获取映射)。 不过,我怀疑,如果图书馆作者更了解图书馆的一般化,这是有据可查的(应用程序编程与影响先于前述),那么我们不会看到这个错误。

其次,map函数是Scala中的一个特例,因为它在for-comprehensions中的使用。 这不幸的是,一个更好装备的图书馆devise师不能忽略这个错误,同时也不会牺牲理解的语法糖。 换句话说,如果Scala库devise人员要摧毁一个方法,那么这个很容易被忽略,但请不要映射!

我希望有人能说出来,因为事实certificate,斯卡拉坚持要犯的错误变得更加困难,显然是出于我强烈反对的原因。 也就是说,“普通程序员不负责任的反对意见”(也就是太难了!)的解决scheme并不是“安抚他们让他们更容易”,而是提供指导和帮助,成为更好的程序员。 我和斯卡拉的目标是争论这个问题,但回到你的观点。

你可能在说明你的观点,预测“普通程序员”的具体回应。 也就是那些会声称“但是太复杂了! 或者一些这样的。 这些是你所指的耶格或布洛赫。 我对这些反思智主义/实用主义运动的人的回应是相当苛刻的,我已经在期待一连串的回应,所以我会省略。

我真的希望斯卡拉图书馆能够改善,至less可以把错误安全地藏在一个angular落里。 Java is a language where "trying to do anything useful" is so incredibly costly, that it is often not worth it because the overwhelming amount of errors simply cannot be avoided. I implore Scala to not go down the same path.

I totally agree with both the question and Martin's answer :). Even in Java, reading javadoc with generics is much harder than it should be due to the extra noise. This is compounded in Scala where implicit parameters are used as in the questions's example code (while the implicits do very useful collection-morphing stuff).

I don't think its a problem with the language per se – I think its more a tooling issue. And while I agree with what Jörg W Mittag says, I think looking at scaladoc (or the documentation of a type in your IDE) – it should require as little brain power as possible to grok what a method is, what it takes and returns. There shouldn't be a need to hack up a bit of algebra on a bit of paper to get it 🙂

For sure IDEs need a nice way to show all the methods for any variable/expression/type (which as with Martin's example can have all the generics inlined so its nice and easy to grok). I like Martin's idea of hiding the implicits by default too.

To take the example in scaladoc…

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

When looking at this in scaladoc I'd like the generic block [B, That] to be hidden by default as well as the implicit parameter (maybe they show if you hover a little icon with the mouse) – as its extra stuff to grok reading it which usually isn't that relevant. eg imagine if this looked like…

 def map(f: A => B): That 

nice and clear and obvious what it does. You might wonder what 'That' is, if you mouse over or click it it could expand the [B, That] text highlighting the 'That' for example.

Maybe a little icon could be used for the [] declaration and (implicit…) block so its clear there are little bits of the statement collapsed? Its hard to use a token for it, but I'll use a . for now…

 def map.(f: A => B).: That 

So by default the 'noise' of the type system is hidden from the main 80% of what folks need to look at – the method name, its parameter types and its return type in nice simple concise way – with little expandable links to the detail if you really care that much.

Mostly folks are reading scaladoc to find out what methods they can call on a type and what parameters they can pass. We're kinda overloading users with way too much detail right how IMHO.

Here's another example…

 def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]): PartialFunction[A1, B1] 

Now if we hid the generics declaration its easier to read

 def orElse(that: PartialFunction[A1, B1]): PartialFunction[A1, B1] 

Then if folks hover over, say, A1 we could show the declaration of A1 being A1 <: A. Covariant and contravariant types in generics add lots of noise too which can be rendered in a much easier to grok way to users I think.

I don't know how to break it to you, but I have a PhD from Cambridge, and I'm using 2.8 just fine.

More seriously, I hardly spent any time with 2.7 (it won't inter-op with a Java library I am using) and started using Scala just over a month ago. I have some experience with Haskell (not much), but just ignored the stuff you're worried about and looked for methods that matched my experience with Java (which I use for a living).

So: I am a "new user" and I wasn't put off – the fact that it works like Java gave me enough confidence to ignore the bits I didn't understand.

(However, the reason I was looking at Scala was partly to see whether to push it at work, and I am not going to do so yet. Making the documentation less intimidating would certainly help, but what surprised me is how much it is still changing and being developed (to be fair what surprised me most was how awesome it is, but the changes came a close second). So I guess what I am saying is that I'd rather prefer the limited resources were put into getting it into a final state – I don't think they were expecting to be this popular this soon.)

Don't know Scala at all, however a few weeks ago I could not read Clojure. Now I can read most of it, but can not write anything yet beyond the most simplistic examples . I suspect Scala is no different. You need a good book or course depending on how you learn. Just reading the map declaration above, I got maybe 1/3 of it.

I believe the bigger problems are not the syntax of these languages, but adopting and internalizing the paradigms that make them usable in everyday production code. For me Java was not a huge leap from C++, which was not a huge leap from C, which was not a leap at all from Pascal, nor Basic etc… But coding in a functional language like Clojure is a huge leap (for me anyway). I guess in Scala you can code in Java style or Scala style. But in Clojure you will create quite the mess trying to keep your imperative habits from Java.

Scala has a lot of crazy features (particularly where implicit parameters are concerned) that look very complicated and academic, but are designed to make things easy to use. The most useful ones get syntactic sugar (like [A <% B] which means that an object of type A has an implicit conversion to an object of type B) and a well-documented explanation of what they do. But most of the time, as a client of these libraries you can ignore the implicit parameters and trust them to do the right thing.

Is this going to put people off coming to Scala?

I don't think it is the main factor that will affect how popular Scala will become, because Scala has a lot of power and its syntax is not as foreign to a Java/C++/PHP programmer as Haskell, OCaml, SML, Lisps, etc..

But I do think Scala's popularity will plateau at less than where Java is today, because I also think the next mainstream language must be much simplified, and the only way I see to get there is pure immutability, ie declarative like HTML, but Turing complete. However, I am biased because I am developing such a language, but I only did so after ruling out over a several month study that Scala could not suffice for what I needed.

Is this going to give Scala a bad name in the commercial world as an academic plaything that only dedicated PhD students can understand? Are CTOs and heads of software going to get scared off?

I don't think Scala's reputation will suffer from the Haskell complex. But I think that some will put off learning it, because for most programmers, I don't yet see a use case that forces them to use Scala, and they will procrastinate learning about it. Perhaps the highly-scalable server side is the most compelling use case.

And, for the mainstream market, first learning Scala is not a "breath of fresh air", where one is writing programs immediately, such as first using HTML or Python. Scala tends to grow on you, after one learns all the details that one stumbles on from the start. However, maybe if I had read Programming in Scala from the start, my experience and opinion of the learning curve would have been different.

Was the library re-design a sensible idea?

当然。

If you're using Scala commercially, are you worried about this? Are you planning to adopt 2.8 immediately or wait to see what happens?

I am using Scala as the initial platform of my new language. I probably wouldn't be building code on Scala's collection library if I was using Scala commercially otherwise. I would create my own category theory based library, since the one time I looked, I found Scalaz's type signatures even more verbose and unwieldy than Scala's collection library. Part of that problem perhaps is Scala's way of implementing type classes, and that is a minor reason I am creating my own language.


I decided to write this answer, because I wanted to force myself to research and compare Scala's collection class design to the one I am doing for my language. Might as well share my thought process.

The 2.8 Scala collections use of a builder abstraction is a sound design principle. I want to explore two design tradeoffs below.

  1. WRITE-ONLY CODE: After writing this section, I read Carl Smotricz's comment which agrees with what I expect to be the tradeoff. James Strachan and davetron5000's comments concur that the meaning of That (it is not even That[B]) and the mechanism of the implicit is not easy to grasp intuitively. See my use of monoid in issue #2 below, which I think is much more explicit. Derek Mahar's comment is about writing Scala, but what about reading the Scala of others that is not "in the common cases".

    One criticism I have read about Scala, is that it is easier to write it, than read the code that others have written. And I find this to be occasionally true for various reasons (eg many ways to write a function, automatic closures, Unit for DSLs, etc), but I am undecided if this is major factor. Here the use of implicit function parameters has pluses and minuses. On the plus side, it reduces verbosity and automates selection of the builder object. In Odersky's example the conversion from a BitSet, ie Set[Int], to a Set[String] is implicit. The unfamiliar reader of the code might not readily know what the type of collection is, unless they can reason well about the all the potential invisible implicit builder candidates which might exist in the current package scope. Of course, the experienced programmer and the writer of the code will know that BitSet is limited to Int, thus a map to String has to convert to a different collection type. But which collection type? It isn't specified explicitly.

  2. AD-HOC COLLECTION DESIGN: After writing this section, I read Tony Morris's comment and realized I am making nearly the same point. Perhaps my more verbose exposition will make the point more clear.

    In "Fighting Bit Rot with Types" Odersky & Moors, two use cases are presented. They are the restriction of BitSet to Int elements, and Map to pair tuple elements, and are provided as the reason that the general element mapping function, A => B, must be able to build alternative destination collection types. However, afaik this is flawed from a category theory perspective. To be consistent in category theory and thus avoid corner cases, these collection types are functors, in which each morphism, A => B, must map between objects in the same functor category, List[A] => List[B], BitSet[A] => BitSet[B]. For example, an Option is a functor that can be viewed as a collection of sets of one Some( object ) and the None. There is no general map from Option's None, or List's Nil, to other functors which don't have an "empty" state.

    There is a tradeoff design choice made here. In the design for collections library of my new language, I chose to make everything a functor, which means if I implement a BitSet, it needs to support all element types, by using a non-bit field internal representation when presented with a non-integer type parameter, and that functionality is already in the Set which it inherits from in Scala. And Map in my design needs to map only its values, and it can provide a separate non-functor method for mapping its (key,value) pair tuples. One advantage is that each functor is then usually also an applicative and perhaps a monad too. Thus all functions between element types, eg A => B => C => D => …, are automatically lifted to the functions between lifted applicative types, eg List[A] => List[B] => List[C] => List[D] => …. For mapping from a functor to another collection class, I offer a map overload which takes a monoid, eg Nil, None, 0, "", Array(), etc.. So the builder abstraction function is the append method of a monoid and is supplied explicitly as a necessary input parameter, thus with no invisible implicit conversions. (Tangent: this input parameter also enables appending to non-empty monoids, which Scala's map design can't do.) Such conversions are a map and a fold in the same iteration pass. Also I provide a traversable, in the category sense, "Applicative programming with effects" McBride & Patterson, which also enables map + fold in a single iteration pass from any traversable to any applicative, where most every collection class is both. Also the state monad is an applicative and thus is a fully generalized builder abstraction from any traversable.

    So afaics the Scala collections is "ad-hoc" in the sense that it is not grounded in category theory, and category theory is the essense of higher-level denotational semantics. Although Scala's implicit builders are at first appearance "more generalized" than a functor model + monoid builder + traversable -> applicative, they are afaik not proven to be consistent with any category, and thus we don't know what rules they follow in the most general sense and what the corner cases will be given they may not obey any category model. It is simply not true that adding more variables makes something more general, and this was one of huge benefits of category theory is it provides rules by which to maintain generality while lifting to higher-level semantics. A collection is a category.

    I read somewhere, I think it was Odersky, as another justification for the library design, is that programming in a pure functional style has the cost of limited recursion and speed where tail recursion isn't used. I haven't found it difficult to employ tail recursion in every case that I have encountered so far.


Additionally I am carrying in my mind an incomplete idea that some of Scala's tradeoffs are due to trying to be both an mutable and immutable language, unlike for example Haskell or the language I am developing. This concurs with Tony Morris's comment about for comprehensions. In my language, there are no loops and no mutable constructs. My language will sit on top of Scala (for now) and owes much to it, and this wouldn't be possible if Scala didn't have the general type system and mutability. That might not be true though, because I think Odersky & Moors ("Fighting Bit Rot with Types") are incorrect to state that Scala is the only OOP language with higher-kinds, because I verified (myself and via Bob Harper) that Standard ML has them. Also appears SML's type system may be equivalently flexible (since 1980s), which may not be readily appreciated because the syntax is not so much similar to Java (and C++/PHP) as Scala. In any case, this isn't a criticism of Scala, but rather an attempt to present an incomplete analysis of tradeoffs, which is I hope germane to the question. Scala and SML don't suffer from Haskell's inability to do diamond multiple inheritance , which is critical and I understand is why so many functions in the Haskell Prelude are repeated for different types.

It seems necessary to state ones degree here: BA in Political Science and B.ed in Computer Science.

To the point:

Is this going to put people off coming to Scala?

Scala is difficult, because its underlying programming paradigm is difficult. Functional programming scares a lot of people. It is possible to build closures in PHP but people rarely do. So no, not this signature but all the rest will put people off, if they do not have the specific education to make them value the power of the underlying paradigm.

If this education is available, everyone can do it. Last year I build a chess computer with a bunch of school kids in SCALA! They had their problems but they did fine in the end.

If you're using Scala commercially, are you worried about this? Are you planning to adopt 2.8 immediately or wait to see what happens?

I would not be worried.

I have a maths degree from Oxford too! It took me a while to 'get' the new collections stuff. But I like it a lot now that I do. In fact, the typing of 'map' was one of the first big things that bugged me in 2.7 (perhaps since the first thing I did was subclass one of the collection classes).

Reading Martin's paper on the new 2.8 collections really helped explain the use of implicits, but yes the documentation itself definitely needs to do a better job of explaining the role of different kind of implicits within method signatures of core APIs.

My main concern is more this: when is 2.8 going to be released? When will the bug reports stop coming in for it? have scala team bitten off more than they can chew with 2.8 / tried to change too much at once?

I'd really like to see 2.8 stabilised for release as a priority before adding anything else new at all, and wonder (while watching from the sidelines) if some improvements could be made to the way the development roadmap for the scala compiler is managed.

What about error messages in use site?

And what about when comes the use case one needs to integrate existing types with a custom one that fits a DSL. One have to be well educated on matters of association, precedence, implicit conversions, implicit parameters, higher kinds, and maybe existential types.

It's very good to know that mostly it's simple but it's not necessarily enough. At least there must be one guy who knows this stuff if widespread library is to be designed.