Scala 2.8馆藏图书馆是“历史上最长的遗书”吗?

Is the Scala 2.8 collections library a case of “the longest suicide note in history”?

我刚刚开始研究即将发布的2.8版本中的scala集合库的重新实现。那些熟悉2.7中的库的人会注意到,从使用的角度来看,库的变化很小。例如。。。

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

…两种版本都可以。图书馆非常有用:事实上,它非常棒。然而,那些以前不熟悉scala并四处摸索以了解该语言的人现在必须理解方法签名,例如:

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

对于如此简单的功能,这是一个令人望而生畏的签名,我发现自己很难理解它。不是我认为斯卡拉有可能成为下一个Java(O/C/C++/C)——我不相信它的创建者瞄准了那个市场——但是我认为Scala成为下一个Ruby或Python(即获得一个重要的商业用户基础)当然是可行的。

  • 这会推迟人们去斯卡拉吗?
  • 这是否会让斯卡拉在商业界名声扫地,成为一个只有专注的博士生才能理解的学术游戏?CTO和软件主管会被吓跑吗?
  • 图书馆是否重新设计了一个明智的想法?
  • 如果你在商业上使用scala,你担心吗?你打算立即采用2.8还是等着看会发生什么?

史蒂夫耶格曾经攻击斯卡拉(在我看来是错误的),因为他认为斯卡拉的类型系统过于复杂。我担心有人会用这个API来扩展FUD(类似于Josh Bloch如何吓唬JCP不向Java添加闭包)。

注意-我应该清楚,虽然我相信Joshua Bloch在拒绝BGGA关闭提议方面具有影响力,但我不认为这是因为他诚实地认为该提议代表了一个错误。


尽管我的妻子和同事一直告诉我什么,我不认为我是个白痴:我有牛津大学的数学学位,并且我已经商业化编程了将近12年,在斯卡拉已经有大约一年了(也是商业化的)。

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


我希望这不是"自杀笔记",但我明白你的意思。您同时了解了什么是scala的优势和问题:它的可扩展性。这使我们能够在库中实现最主要的功能。在其他一些语言中,像mapcollect这样的序列是内置的,没有人需要看到编译器必须经历的所有循环才能使它们顺利工作。在scala中,它都在一个库中,因此是开放的。

实际上,由复杂类型支持的map的功能相当先进。考虑一下:

1
2
3
4
5
6
7
8
9
10
11
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!)

看看你怎么总是得到最好的类型?如果你把Ints映射到Ints,你会得到一个BitSet,但是如果你把Ints映射到Strings,你会得到一个Set。映射结果的静态类型和运行时表示形式都取决于传递给它的函数的结果类型。即使集合是空的,也可以这样做,所以函数永远不会被应用!据我所知,没有其他具有同等功能的收集框架。然而,从用户的角度来看,这就是事情应该如何工作的。

我们所面临的问题是,所有使这种情况发生的聪明技术都会泄漏到类型签名中,而类型签名变得巨大而可怕。但在默认情况下,用户不应该显示map的完整类型签名?如果她在BitSet中查到map,她会得到:

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

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

我们还没有在工具中实现这样的功能。但我相信我们需要这样做,以避免吓跑人们,并提供更多有用的信息。有了这样的工具,希望智能框架和库不会变成自杀笔记。


我既没有博士学位,也没有其他任何学位,既没有理科学位,也没有数学学位,甚至没有其他任何领域的学位。我没有使用scala或其他类似语言的经验。我甚至没有远程可比类型系统的经验。事实上,我所知道的唯一一种语言,不仅仅是表面上的知识,甚至还有一个类型系统,那就是帕斯卡语,它并不以其复杂的类型系统而闻名。(虽然它确实有范围类型,但这与其他语言几乎没有关系),我知道的其他三种语言是basic、smalltalk和ruby,它们都没有类型系统。好的。

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

在这种情况下,map实际上不需要集合是一个列表,也不需要被订购、可排序或类似的东西。map关心的唯一一件事是,它可以一个接一个地访问集合中的所有元素,但不能按特定顺序访问。它不需要知道结果集合是什么,只需要知道如何构建它。所以,这就是它的类型签名所需要的。好的。

所以,而不是好的。

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

它是map的传统类型签名,一般不需要具体的List数据结构,只需要IterableLike数据结构好的。

1
map :: (IterableLike i, IterableLike j) ? (a → b) → i → j

然后进一步概括,只要求存在一个可以将结果转换为用户想要的任何数据结构的函数:好的。

1
map :: IterableLike i ? (a → b) → i → ([b] → c) → c

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

1
def map[B](f: (A) ? B): List[B]

这是map的传统签名。(注意,由于scala的面向对象特性,输入列表参数是如何消失的,因为它现在是单个调度OO系统中每个方法都具有的隐式接收参数。)然后,它从具体的List推广到更一般的IterableLike。好的。

1
def map[B](f: (A) ? B): IterableLike[B]

现在,它用一个函数来替换IterableLike结果集合,这个函数可以产生几乎所有的结果。好的。

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

我相信这并不难理解。实际上,您只需要几个智能工具:好的。

  • 你需要大致知道map是什么。我承认,如果只给出没有方法名的类型签名,就很难搞清楚到底发生了什么。但是,既然您已经知道了map应该做什么,并且知道了它的类型签名应该是什么,那么您可以快速扫描签名并关注异常情况,例如"为什么这个map采用两个函数作为参数,而不是一个函数?"
  • 您需要能够实际读取类型签名。但是,即使您以前从未见过scala,这也应该很容易,因为它实际上只是您已经从其他语言了解的类型语法的混合体:vb.net使用方括号表示参数多态性,使用箭头表示返回类型,使用冒号分隔名称和类型,这实际上是规范。
  • 您需要大致了解通用编程是关于什么的。(这并不难理解,因为它基本上都是以名称来表达的:它实际上只是以一种通用的方式进行编程)。
  • 这三个都不应该给任何专业的甚至是业余的程序员带来严重的头痛。EDCOX1·0是过去50年中设计的几乎所有语言的标准函数,不同语言具有不同语法的事实对于任何设计HTML和CSS网站的人来说是显而易见的,如果没有TH的一些恼人的C++粉丝,你就不能订阅一个远程编程相关的邮件列表。圣斯蒂芬诺夫教会解释通用编程的优点。好的。

    是的,scala很复杂。是的,scala拥有人类所知的最复杂的类型系统之一,可以与haskell、miranda、clean或cyclone等语言竞争甚至超越。但是如果复杂性是一个反对编程语言成功的理由,C++早就死了,我们都将是编写方案。scala很可能不会成功有很多原因,但是程序员在坐在键盘前不必费心打开大脑这一事实可能不是主要原因。好的。好啊。


    同样的事情在C++中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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;
    }


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

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

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

    考虑到这些,我认为:

  • 任何人(像大多数人一样)遇到过这种类型签名,如果他们预先对它进行了处理,他们将永远模仿scala,如果他们喜欢scala,他们将把它视为scala力量的象征。

  • 如果文档没有得到增强以提供使用示例并清楚地解释方法的用途和使用方法,那么它可能会稍微降低对scala的采用。

  • 从长远来看,这无关紧要。scala可以做类似的事情,这将使为scala编写的库更强大,使用更安全。这些库和框架将吸引程序员使用强大的工具。

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

  • 唉,Java程序员非常喜欢电动工具,所以,在回答这个问题时,我刚刚修改了我对主流Scala采用的期望。毫无疑问,scala将成为主流语言。不是C主流,但可能是Perl主流或PHP主流。

    说到Java,你曾经取代过类加载器吗?你有没有研究过这涉及到什么?如果你看看框架作者写的地方,Java可能会很吓人。只是大多数人不这样做。同样的事情也适用于斯卡拉,伊莫霍,但早期的收养者有一种倾向,看他们遇到的每一块岩石下面,看是否有什么东西隐藏在那里。


    Is this going to put people off coming to Scala?

    是的,但它也会阻止人们被推迟。自从scala获得了对更高类型的支持以来,我认为缺少使用更高类型的集合是一个主要的弱点。它使API文档更加复杂,但它确实使使用更加自然。

    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?

    有些人可能会。我不认为许多"专业"的开发人员可以访问scala,部分原因是scala的复杂性,部分原因是许多开发人员不愿意学习。雇用这些开发人员的首席技术官将被吓跑。

    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?

    我不是在商业上使用它。我可能要等到2.8.x系列中至少有几次版本的版本,然后再尝试引入它,以便清除bug。我还将拭目以待,看看EPFL在改进其开发A发布过程方面取得了多大的成功。我所看到的似乎充满希望,但我为一家保守的公司工作。

    "scala对主流开发人员来说太复杂了吗?"…

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

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


    scala社区能够帮助缓解对scala新手的恐惧的一个方法是专注于实践和以身作则的教学——很多例子都是从小开始逐渐变大的。以下是采用这种方法的几个站点:

    • 每日鳞片
    • 小口学习鳞片
    • 简单斯卡拉

    在这些站点上花费了一段时间后,人们很快意识到,尽管scala及其库可能难以设计和实现,但使用起来并不那么困难,特别是在常见的情况下。


    我在美国一所廉价的"大众市场"大学获得了本科学位,所以我会说我处于用户智能(或者至少是教育程度)的中间:我刚刚涉足了scala几个月,并且已经开发了两三个非平凡的应用程序。

    特别是现在Intellij已经发布了他们的优秀的IDE,imho是目前最好的scala插件,scala的开发相对来说比较轻松:

    • 我发现我可以使用斯卡拉作为"没有分号的Java",也就是说,我写类似的代码,我将在爪哇做什么,并受益于句法简洁,如通过类型推理所获得的。异常处理,当我这样做的时候,更方便。没有getter/setter样板文件,类定义就不那么冗长了。

    • 有时,我设法写一行来完成相当于多行的Java。在适用的情况下,功能方法链,如地图、折叠、收集、过滤等,是有趣的组成和优雅的观赏。

    • 我很少能从scala更强大的特性中获益:闭包和部分(或curried)函数、模式匹配…有点像。

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

    这只是我的观点:我觉得斯卡拉具有C++的强大性,与C++的复杂性和可读性相结合。语言的语法复杂性也使得API文档难以阅读。

    斯卡拉在很多方面都很有思想和才华。我怀疑许多学者会喜欢用它来编程。然而,它也充满了智慧和魅力,它比Java具有更高的学习曲线,而且更难阅读。如果我扫描一下FRA,看看有多少开发人员仍在苦苦思索Java的细微之处,我无法想象斯卡拉成为主流语言。没有一家公司能够证明,当他们以前只需要1周的Java课程时,就可以将开发者发送3周的Scala课程。


    我认为这种方法的主要问题是(implicit bf : CanBuildFrom[Repr, B, That])没有任何解释。尽管我知道什么是隐式参数,但没有任何东西表明这对调用有何影响。仔细研究scaladoc只会让我更加困惑(很少有与CanBuildFrom相关的类甚至有文档)。

    我认为一个简单的"在bf的范围内必须有一个隐式对象,它为B类型的对象提供一个构建器,使其进入返回类型That中",会有所帮助,但当您真正想做的是将A映射到B时,这是一个令人兴奋的概念。事实上,我不确定这是正确的,因为我确实这样做了。不知道Repr是什么意思,Traversable的文档毫无线索。

    所以,我有两个选择,两个都不愉快:

    • 假设它只适用于旧地图的工作方式,以及地图在大多数其他语言中的工作方式。
    • 再深入研究一下源代码

    我知道斯卡拉基本上是在揭示这些东西是如何工作的,最终这是一种方式来做什么牛轭湖描述。但这会分散签名的注意力。


    我是一个scala初学者,老实说,我不认为类型签名有问题。参数是要映射的函数,隐式参数是生成器返回正确集合的参数。清晰易读。

    事实上,整件事都很优雅。生成器类型参数允许编译器选择正确的返回类型,而隐式参数机制则向类用户隐藏该额外参数。我试过这个:

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

    这就是多态性。

    现在,当然,这不是一个主流模式,它会吓跑许多人。但是,它也会吸引许多重视它的表现力和优雅的人。


    不幸的是,你给地图的签名是一个错误的地图,确实有合法的批评。

    第一个批评是,通过颠覆地图的签名,我们得到了一些更普遍的东西。认为这是默认的美德是一个常见的错误。它不是。映射函数被很好地定义为一个协变函数fx->(x->y->fy,并遵循组合和同一性的两个定律。任何其他归因于"地图"的东西都是一种嘲弄。

    给定的签名是其他东西,但它不是映射。我怀疑它试图做的是一个专门的、稍微修改过的"遍历"签名版本,这是迭代器模式的本质。签字如下:

    1
    traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)

    我将把它转换成scala:

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

    当然失败了——还不够全面!另外,它也略有不同(请注意,通过"标识"函数运行"遍历"可以获得地图)。但是,我怀疑,如果库作者更清楚地了解有良好文档记录的库常规化(具有上述效果的应用程序设计),那么我们不会看到此错误。

    其次,map函数在scala中是一种特殊情况,因为它用于理解。不幸的是,这意味着一个装备更好的图书馆设计师不能忽视这个错误,同时也不能牺牲理解的句法糖分。换句话说,如果scala库的设计者要销毁一个方法,那么这很容易被忽略,但请不要映射!

    我希望有人对这件事发表意见,因为事实上,解决斯卡拉坚持犯下的错误会变得更加困难,显然是因为我强烈反对的原因。也就是说,解决"普通程序员不负责任的反对意见(即太难了!)"的方法。不是"安抚他们让他们更容易",而是提供指针和帮助,让他们成为更好的程序员。我和斯卡拉的目标在这个问题上是有争议的,但回到你的观点上来。

    你可能是在说你的观点,预测"普通程序员"的具体反应。也就是说,那些会声称"但这太复杂了"的人。或者一些这样的。这些是您所指的Yegges或Bloch。我对反智主义/实用主义运动的这些人的反应相当严厉,我已经预料到了一连串的反应,所以我将忽略它。

    我真的希望scala库能够改进,或者至少可以安全地将错误隐藏在角落里。Java是一种"试图做任何有用的事情"的语言是如此的昂贵,以至于它常常不值得,因为绝大多数的错误是无法避免的。我恳求斯卡拉不要走同一条路。


    我完全同意这个问题和马丁的回答:)。即使在爪哇,用泛型来阅读JavaDoc比由于额外的噪音要困难得多。这在scala中更为复杂,其中隐式参数被用作问题的示例代码(而隐式参数在集合变形方面非常有用)。

    我不认为这是语言本身的问题——我认为这更像是工具问题。当我同意J?rg w mittag说,我想看看scaladoc(或者你的IDE中的一个类型的文档),它应该需要尽可能少的脑力来摸索一个方法是什么,它需要什么和返回什么。为了得到它,不需要在一张纸上拼凑一点代数。)

    当然,IDES需要一种很好的方法来显示任何变量/表达式/类型的所有方法(就像马丁的例子一样,它可以内联所有泛型,所以它很好,也很容易搜索)。我也喜欢马丁在默认情况下隐藏暗示的想法。

    以scaladoc为例…

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

    当在scaladoc中查看这个时,我希望在默认情况下隐藏通用块[b,that]以及隐式参数(如果用鼠标悬停一个小图标,可能会显示这些参数),作为额外的东西来阅读它,这通常不太相关。想象一下如果这看起来像…

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

    很好,很清楚,很明显。你可能会想知道"那"是什么,如果你用鼠标或点击它,它可以展开[b,那个]文本,例如突出显示"那"。

    也许有一个小图标可以用于[]声明和(隐式的…)块,这样就可以清楚地看到语句中有一些折叠的部分了?它很难使用令牌,但我将使用。现在…

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

    因此,默认情况下,类型系统的"噪声"对人们需要查看的主要内容(方法名、参数类型及其返回类型)隐藏了80%——如果你真的那么在意的话,它几乎没有可扩展的细节链接。

    大多数人都在阅读scaladoc,以了解他们可以对类型调用什么方法,以及可以传递什么参数。我们有点用太多的细节让用户超载了。

    这是另一个例子…

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

    现在,如果我们隐藏了泛型声明,就更容易阅读了

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

    然后,如果人们悬停在上面,比如说,a1,我们可以显示a1是a1<:a的声明。泛型中的协变和反变类型也会添加大量的噪声,我认为这些噪声会以一种更容易搜索到用户的方式呈现出来。


    我不知道该怎么跟你说,但是我有剑桥大学的博士学位,而且我用的是2.8,很好。

    更严重的是,我几乎没有花2.7的时间(它不会和我使用的Java库交互),一个月前开始使用Scala。我对Haskell有一些经验,但忽略了你担心的东西,寻找与我的Java经验相匹配的方法(我用它来谋生)。

    所以,我是一个"新用户",我没有被推迟——事实上它像Java给了我足够的信心去忽略我不理解的比特。

    (然而,我看斯卡拉的部分原因是想看看是否要在工作中推动它,我现在还不打算这样做。让文档不那么吓人当然会有所帮助,但让我吃惊的是它仍然在变化和发展(公平地说,最让我吃惊的是它是多么的棒,但变化是近一秒来的)。所以我想我想说的是,我宁愿把有限的资源投入到最终的状态中去——我认为他们不希望这么快就会受到欢迎。)


    根本不认识斯卡拉,但几周前我还看不懂《Clojure》。现在我可以读大部分,但除了最简单的例子外,我还不能写任何东西。我想斯卡拉也没什么不同。你需要一本好的书或课程,这取决于你如何学习。刚刚看了上面的地图声明,我大概得到了三分之一。

    我相信更大的问题不是这些语言的语法,而是采用和内化使它们在日常生产代码中可用的范例。对我来说,Java并不是一个巨大的跳跃,它不是来自C的巨大飞跃,它不是帕斯卡的跳跃,也不是基本的…但是像Clojure这样的函数式语言的编码是一个巨大的飞跃(无论如何对我来说)。我猜想在Scala中,你可以用Java风格或Scala风格来编码。但在Culjule中,你会创造出一个混乱的状态,试图让你的命令习惯远离Java。


    scala有很多疯狂的特性(特别是涉及到隐式参数的特性),看起来非常复杂和学术化,但设计的目的是使事物易于使用。最有用的是句法糖分(如[A <% B],这意味着类型A的对象有一个隐式转换为类型B的对象)以及对它们所做工作的有充分记录的解释。但大多数时候,作为这些库的客户机,您可以忽略隐式参数,并相信它们会做正确的事情。


    Is this going to put people off coming to Scala?

    Ok.

    我不认为这是影响Scala的流行程度的主要因素,因为Scala有很多的力量,它的语法对于Haskell、OcAML、SML、Lisp等Java/C++/PHP程序员来说并不陌生。好的。

    但我确实认为斯卡拉的人气将比今天的Java要低,因为我也认为下一个主流语言必须被简化,我看到的唯一的方式是纯不变的,即声明式的HTML,但是图灵完成。然而,我有偏见,因为我正在开发这样一种语言,但我只是在排除了几个月的研究后,斯卡拉不能满足我需要。好的。

    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?

    Ok.

    我不认为斯卡拉的名声会受到哈斯克尔情结的影响。但我认为有些人会推迟学习它,因为对于大多数程序员来说,我还没有看到迫使他们使用scala的用例,他们会拖延学习。也许高度可伸缩的服务器端是最引人注目的用例。好的。

    而且,对于主流市场来说,第一次学习scala并不是"新鲜空气的呼吸",在这里人们可以立即编写程序,比如首先使用html或python。斯卡拉倾向于在你身上成长,在你从一开始就了解到所有你绊倒的细节之后。然而,如果我从一开始就阅读scala中的编程,我对学习曲线的经验和看法可能会有所不同。好的。

    Was the library re-design a sensible idea?

    Ok.

    一定地。好的。

    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?

    Ok.

    我使用scala作为新语言的初始平台。如果我在商业上使用scala,我可能不会在scala的集合库上构建代码。我将创建自己的基于类别理论的库,因为有一次我发现scalaz的类型签名比scala的集合库更加冗长和笨拙。这个问题的一部分可能是scala实现类型类的方式,这也是我创建自己语言的一个次要原因。好的。

    我决定写下这个答案,因为我想强迫自己去研究和比较斯卡拉的收集类设计和我为我的语言所做的设计。不妨分享一下我的思考过程。好的。

    2.8scala集合使用构建器抽象是一个合理的设计原则。我想探讨下面的两个设计权衡。好的。

  • 只写代码:在写完这一部分之后,我阅读了卡尔·斯莫特里茨的评论,这与我期望的权衡一致。詹姆斯·斯特拉坎和达夫特隆5000的评论一致认为,这一点的意义(甚至不是这样)和内隐的机制不容易直观地理解。见下面第二期中我对monoid的使用,我认为这更为明确。DerekMahar的评论是关于编写scala的,但是关于阅读其他非"常见情况"的scala呢?好的。

    我读到的关于scala的一个批评是,写它比读别人写的代码容易。由于各种原因(例如,许多编写函数的方法、自动闭包、DSL单元等),我发现这偶尔也是正确的,但如果这是主要因素,我还不确定。在这里,隐式函数参数的使用有正负之分。另一方面,它减少了冗长的内容并自动选择生成器对象。在Odersky的示例中,从一个位集(即set[int]到set[string]的转换是隐式的。不熟悉的代码阅读器可能不容易知道集合的类型,除非他们能够很好地解释当前包范围内可能存在的所有潜在的不可见隐式构建器候选项。当然,经验丰富的程序员和代码编写者会知道位集仅限于int,因此映射到字符串必须转换为不同的集合类型。但哪种收藏类型呢?没有明确指定。好的。

  • 特设收藏设计:写完这一部分后,我阅读了托尼·莫里斯的评论,意识到我的观点几乎是一样的。也许我更详细的阐述会使这一点更清楚。好的。

    在"与类型战斗的比特腐烂"或"摩尔"中,给出了两个用例。它们是位集对int元素和映射对tuple元素的限制,并作为一般元素映射函数a=>b必须能够构建可选目标集合类型的原因提供。然而,阿法克从范畴理论的角度来看,这是有缺陷的。为了在范畴理论上保持一致,从而避免出现角点情况,这些集合类型是函子,其中每个态a=>b必须在同一函子类别、list[a]=>list[b]、bitset[a]=>bitset[b]中的对象之间映射。例如,一个选项是一个函数,它可以被视为一个some(object)和none的集合。没有从选项的none或list的nil到其他没有"空"状态的函数的常规映射。好的。

    这里有一个折衷的设计选择。在我的新语言的集合库设计中,我选择了使所有内容都成为一个函数,这意味着如果我实现一个位集,它需要支持所有元素类型,在使用非整数类型参数时使用一个非位字段内部表示,并且该功能已经在它从scala继承的集合中。在我的设计中,map只需要映射它的值,它可以提供一个单独的非函数方法来映射它的(key,value)对元组。一个优点是,每个函数通常也是一个应用程序,也许也是一个单胞函数。因此,元素类型之间的所有函数,例如a=>b=>c=>d=>…,都会自动提升到提升的应用类型之间的函数,例如list[a]=>list[b]=>list=>list[d]=>…。对于从一个函数映射到另一个集合类,我提供了一个采用monoid的映射重载,例如nil、none、0、"、array()等。因此,builder抽象函数是monoid的append方法,它作为必需的输入参数显式提供,因此没有不可见的隐式转换。(切线:这个输入参数还可以附加到非空的monoids,scala的地图设计不能这样做。)这样的转换是同一迭代过程中的一个地图和一个折叠。此外,我还提供了一个类别意义上的可遍历"带效果的应用程序"mcbride&patterson,它还允许在一次迭代中从任何可遍历传递到任何应用程序,其中大多数集合类都是。另外,state monad是一个应用程序,因此是任何可遍历的完全通用的生成器抽象。好的。

    因此,从范畴论的角度来看,scala集合是"特设"的,范畴论是更高层次的外延语义学的本质。尽管scala的隐式构建器一开始看起来比函数模型+monoid builder+可遍历->应用程序"更通用",但它们并没有被证明与任何类别一致,因此我们不知道它们在最一般的意义上遵循什么规则,也不知道它们将被赋予什么样的角情况,它们可能不服从任何类别。Y模型。增加更多的变量使事情变得更普遍,这是不正确的,这是范畴理论的一个巨大好处,它提供了在提升到更高层次语义的同时保持普遍性的规则。集合是一个类别。好的。

    我在某个地方读到过,我认为这是Odersky,作为库设计的另一个理由,它是纯函数式的编程在不使用尾部递归的情况下具有有限的递归成本和速度。到目前为止,在我遇到的每种情况下,都不难使用尾部递归。好的。型百万千克1

    此外,我在头脑中还带着一个不完整的想法,即scala的某些权衡是由于试图同时成为一种可变和不变的语言,而不像haskell或我正在开发的语言。这与托尼·莫里斯关于理解的评论是一致的。在我的语言中,没有循环和可变结构。我的语言将位于scala之上(目前为止),并且要归功于它,如果scala没有通用的类型系统和可变性,这是不可能的。不过,这可能不是真的,因为我认为Odersky&moors("用类型来战斗比特腐烂")不正确地说scala是唯一一种更高级的OOP语言,因为我(通过Bob Harper)验证了标准ML拥有它们。同样,SML的类型系统也可能是相当灵活的(自20世纪80年代以来),这可能不容易理解,因为语法与Java(斯卡拉)和C++/PHP没有太多相似之处。无论如何,这不是对scala的批评,而是试图对权衡进行不完整的分析,我希望这与问题有密切关系。scala和sml并没有因为haskell无法进行菱形多重继承而受到影响,这一点很重要,我理解为什么haskell前奏曲中的许多函数会针对不同类型重复出现。好的。型好啊。


    这里似乎有必要说明一个学位:政治学学士学位和计算机科学学士学位。

    切中要害:

    Is this going to put people off coming to Scala?

    scala很难实现,因为它的底层编程范式很难实现。函数式编程吓坏了很多人。在PHP中构建闭包是可能的,但人们很少这样做。所以,不,不是这个签名,而是其他所有的签名都会让人感到厌烦,如果他们没有特定的教育,使他们重视底层范式的力量。

    如果有这种教育,每个人都能做到。去年我和一群学生在斯卡拉做了一台国际象棋电脑!他们有问题,但最后还是做得很好。

    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?

    我不会担心的。


    我也有牛津大学的数学学位!我花了一段时间才弄到新收藏的东西。但现在我很喜欢。事实上,在2.7中输入"map"是第一个困扰我的大问题(可能是因为我做的第一件事是集合类中的一个子类)。

    阅读Martin关于新的2.8集合的论文确实有助于解释隐含的使用,但是文档本身确实需要更好地解释不同类型的隐含在核心API的方法签名中的作用。

    我主要关心的是:2.8什么时候发布?这个bug报告什么时候会停止出现?斯卡拉团队被咬得比他们能咀嚼的多吗?2.8/试图一次改变太多?

    我真的很想看到2.8在添加任何新的东西之前已经稳定下来作为发布的优先级,并且想知道(在旁观的时候)是否可以对scala编译器的开发路线图的管理方式进行一些改进。


    使用站点中的错误消息呢?

    当用例出现时,需要将现有类型与适合DSL的自定义类型集成起来,这又会怎样呢?一个人必须在关联、优先、内隐转换、内隐参数、更高级的类型和可能存在的类型等方面接受良好的教育。

    这是很好地知道,大部分是简单的,但它不一定是足够的。如果要设计一个大面积的图书馆,至少必须有一个人知道这些东西。