我刚刚开始关注即将发布的 2.8 版本中的Scala collections library re-implementation。熟悉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(即获得重要的商业用户群)是可行的
Steve Yegge once attacked Scala(在我看来是错误的)他所看到的过于复杂的类型系统。我担心有人会在这个API上传播FUD字段日(类似于Josh Bloch如何将JCP吓到了为Java添加闭包)。
注意 - 我应该清楚,虽然我认为Joshua Bloch对拒绝BGGA封闭提案有影响,但我不会将此归因于其他任何其他内容而不是他诚实地认为这个提案代表了一个错误。
尽管我的妻子和同事一直在告诉我,我不认为我是个白痴:我从University of Oxford获得了很好的数学学位,而且我的商业编程已有近12年的历史在Scala约一年(也是商业上)。
请注意,炎症主题标题是20世纪80年代早期的quotation made about the manifesto of a UK political party 。这个问题是主观的,但这是一个真实的问题,我已经做了CW,我想就此事提出一些看法。
答案 0 :(得分:871)
我希望这不是“遗书”,但我能看出你的观点。您同时了解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
1}}。 map的结果的静态类型和运行时表示都取决于传递给它的函数的结果类型。即使该集合为空,这也有效,因此该函数永远不会应用!据我所知,没有其他具有同等功能的集合框架。然而,从用户的角度来看,这就是假设的工作方式。
我们遇到的问题是,所有使这种情况发生的聪明技术都会泄漏到类型签名中,这些签名变得庞大而可怕。但是,默认情况下,用户不应该显示map
的完整类型签名?如果她在map
中查到BitSet
,那该怎么办:
map(f: Int => Int): BitSet (click here for more general type)
文档不会出现在这种情况下,因为从用户的角度来看,map确实具有类型(Int => Int) => BitSet
。但是map
也有一个更通用的类型,可以通过点击另一个链接进行检查。
我们的工具尚未实现此类功能。但我相信我们需要做到这一点,以避免吓跑人们并提供更多有用的信息。有了这样的工具,希望智能框架和图书馆不会成为自杀笔记。
答案 1 :(得分:219)
答案 2 :(得分:168)
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;
}
答案 3 :(得分:69)
好吧,我可以理解你的痛苦,但是,坦率地说,像你我这样的人 - 或几乎任何常规的Stack Overflow用户 - 都不是常规。
我的意思是......大多数程序员都不会关心那种类型的签名,因为他们永远不会看到它们!他们不会阅读文档。
只要他们看到代码如何工作的一些示例,并且代码在生成他们期望的结果时没有让他们失败,他们就不会查看文档。如果失败,他们会查看文档,并希望在顶部看到用法示例。
考虑到这些事情,我认为:
任何遇到过该类型签名的人(如在大多数人中)都会模仿Scala,如果他们预先对付Scala,他们会认为它是Scala强大的象征,如果他们喜欢Scala的话。
如果没有增强文档以提供用法示例并清楚地解释方法的用途以及如何使用它,那么它可能会减损Scala的采用。
从长远来看,没关系。 Scala 可以做这样的事情会使为Scala编写的库更强大,更安全。这些库和框架将吸引程序员使用强大的工具。
喜欢简单和直接的程序员将继续使用PHP或类似的语言。
说到Java,你有没有替换类加载器?你有没有看过这涉及到什么?如果你看看框架编写者所做的那些,Java可能会很吓人。只是大多数人没有。同样的事情适用于Scala,恕我直言,但早期采用者倾向于在他们遇到的每块岩石下面看,看看是否有隐藏在那里的东西。
答案 4 :(得分:55)
这是否会让人们去Scala?
是的,但它也会阻止人们被推迟。自从Scala获得对更高级别类型的支持以来,我一直认为缺少使用较高级别类型的集合是一个主要的弱点。它使API文档更复杂,但它确实使用更自然。
这会给scala在商业世界中一个坏名字作为学术玩具,只有专门的博士生才能理解吗? CTO和软件负责人是否会受到惊吓?
有些人可能会。我不认为Scala可以被许多“专业”开发人员访问,部分原因是Scala的复杂性,部分原因是许多开发人员不愿意学习。雇用这些开发人员的首席技术官将被吓跑。
图书馆重新设计了一个明智的想法吗?
绝对。它使得集合与其他语言和类型系统相比更加合适,即使它仍然有一些粗糙的边缘。
如果你在商业上使用scala,你是否担心这个?您是打算立即采用2.8还是等待看看会发生什么?
我没有在商业上使用它。我可能要等到至少两次转入2.8.x系列之后才尝试引入它以便可以刷出错误。我还要等一下,看看EPFL在改进发展过程中取得多大成功。我所看到的看起来很有希望,但我为一家保守的公司工作。
一个更普遍的主题是“Scala对主流开发者来说太复杂了吗?”......
大多数主流或其他开发人员都在维护或扩展现有系统。这意味着他们使用的大部分内容都是由很久以前做出的决定决定的。还有很多人在写COBOL。
明天的主流开发人员将负责维护和扩展当前正在构建的应用程序。其中许多应用程序并非由主流开发人员构建。明天的主流开发人员将使用当今最成功的新应用程序开发人员使用的语言。
答案 5 :(得分:46)
Scala社区可以帮助减轻Scala新手程序员的恐惧的一种方法是专注于练习并通过示例进行教学 - 很多例子从小开始逐渐变大。以下是一些采用这种方法的网站:
在这些网站上花了一些时间后,很快就意识到Scala及其库虽然可能很难设计和实现,但并不难以使用,特别是在常见情况下。
答案 6 :(得分:41)
我拥有一所廉价的“大众市场”美国大学的本科学位,所以我说我处于用户智能(或至少是教育)规模的中间:)我一直在考虑使用Scala几个月后,他们开发了两三个非平凡的应用程序。
特别是现在IntelliJ已经发布了他们的精美IDE,其中恕我直言是目前最好的Scala插件,Scala开发相对无痛:
我发现我可以使用Scala作为“没有分号的Java”,即我在Java中编写类似于代码的代码,并从语法简洁中获益,例如通过类型推断获得的语法简洁。异常处理,当我这样做时,更方便。没有getter / setter样板,类定义就不那么冗长了。
偶尔我会设法写一行来完成相当于多行的Java。在适用的情况下,地图,折叠,收集,过滤等功能方法链很有趣,构图和优雅。
我很少发现自己受益于Scala更强大的功能:闭包和部分(或咖喱)功能,模式匹配......那种事情。
作为一个新手,我继续挣扎于简洁和惯用语法。没有参数的方法调用不需要括号,除非他们这样做;匹配语句中的个案需要一个粗箭头(=>
),但也有一些地方需要一个细箭头(->
)。许多方法都有简短但相当神秘的名称,如/:
或\:
- 如果我翻转足够的手册页,我可以完成我的工作,但我的一些代码最终看起来像Perl或线路噪音。具有讽刺意味的是,最常用的句法速记之一在行动中缺失:我一直被Int
没有定义++
方法的事实所困扰。
这只是我的观点:我觉得Scala具有C ++的强大功能以及C ++的复杂性和可读性。语言的语法复杂性也使得API文档难以阅读。
Scala非常精心并且在很多方面都很出色。我怀疑很多学者都喜欢在其中编程。然而,它也充满了聪明和陷阱,它具有比Java更高的学习曲线,并且更难阅读。如果我扫描论坛,看看有多少开发人员仍然在努力克服Java的优点,我无法想象Scala会成为主流语言。没有公司能够证明派遣开发人员参加为期3周的Scala课程,而之前他们只需要1周的Java课程。
答案 7 :(得分:32)
我认为该方法的主要问题是(implicit bf : CanBuildFrom[Repr, B, That])
没有任何解释。即使我知道隐含的参数是什么,但没有任何迹象表明这会如何影响调用。追逐scaladoc只会让我更加困惑(与CanBuildFrom
相关的几个类甚至都有文档)。
我认为一个简单的“bf
范围内必须有一个隐式对象,为B
类型的对象提供构建器到返回类型That
”会有所帮助,但是当你真正想做的就是将A
映射到B
时,这是一个令人兴奋的概念。事实上,我不确定这是对的,因为我不知道Repr
的类型是什么意思,Traversable
的文档肯定没有任何线索。
所以,我有两个选择,他们都不愉快:
我知道Scala基本上暴露了这些东西如何运作的内容,并且最终这提供了一种方法来执行oxbow_lakes所描述的内容。但这是签名中的分心。
答案 8 :(得分: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")
这种多态性是正确的。
现在,被授予,它不是主流范式,它会吓跑很多人。但是,它也会吸引许多重视其表现力和优雅的人。
答案 9 :(得分:20)
不幸的是,你提供的地图签名对于地图来说是不正确的,并且确实有合法的批评。
第一个批评是通过颠覆地图的签名,我们有一些更普遍的东西。认为默认情况下这是一种美德是一种常见的错误。事实并非如此。地图函数被很好地定义为协变函子Fx - &gt; (x - &gt; y) - &gt;坚持遵守组成和身份的两个定律。任何归因于“地图”的东西都是一种讽刺。
给定的签名是别的,但它不是地图。我怀疑它试图成为一个专门的,略微改变版本的“遍历”签名来自论文,迭代模式的本质。这是它的签名:
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]
当然它失败了 - 它不够通用!此外,它略有不同(请注意,您可以通过身份仿函数运行遍历来获取映射)。但是,我怀疑如果库编写者更了解库文档的一般化,这些文章已经很好地记录了(应用程序编程与前面提到的效果),那么我们就不会看到这个错误了。
其次,map函数是Scala中的一个特例,因为它用于for-comprehensions。不幸的是,这意味着装备更好的图书馆设计师不能忽视这个错误而不会牺牲理解的语法糖。换句话说,如果Scala库设计者要破坏一个方法,那么这很容易被忽略,但请不要映射!
我希望有人能够说出来,因为事实上,解决Scala坚持做出的错误会变得更加困难,显然是出于我强烈反对的原因。也就是说,“普通程序员不负责任的反对意见(即太难!)”的解决方案不是“安抚他们让他们更容易”,而是提供指导和帮助以成为更好的程序员。我自己和斯卡拉的目标是在这个问题上争论,但回到你的观点。
你可能会提出自己的观点,预测“普通程序员”的具体回答。也就是说,那些会声称“但太复杂了!”的人。或者其他一些。这些是你所指的Yegges或Blochs。我对反智主义/实用主义运动的这些人的回应非常苛刻,我已经预料到会有一连串的反应,所以我会省略它。
我真的希望Scala库可以改进,或者至少可以将错误安全地隐藏在角落里。 Java是一种语言,“尝试做任何有用的事情”是如此昂贵,以至于它通常是不值得的,因为绝大多数错误根本无法避免。我恳请Scala不要走同样的道路。
答案 10 :(得分:15)
我完全同意这个问题和马丁的回答:)。即使在Java中,由于额外的噪声,使用泛型读取javadoc也比应该更难。这在Scala中是复杂的,其中隐含参数被用在问题的示例代码中(而implicits做非常有用的收集变形的东西)。
我认为语言本身并不存在问题 - 我认为这更像是一个工具问题。虽然我同意JörgWMittag所说的内容,但我认为看看scaladoc(或IDE中某种类型的文档) - 它应该需要尽可能少的脑力来确定方法是什么,需要什么以及返回什么。为了得到它,不应该需要在一些纸上修改一些代数:)
确定IDE需要一种很好的方式来显示任何变量/表达式/类型的所有方法(与Martin的示例一样,可以将所有泛型内联,因此它很好且易于理解)。我也喜欢Martin默认隐藏隐含的想法。
以scaladoc为例...
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That
在scaladoc中查看此内容时,我希望默认隐藏通用块[B,That]以及隐含参数(如果你用鼠标悬停一个小图标,它们可能显示) - 作为额外的读取它的东西通常不相关。例如想象一下,如果这看起来像......
def map(f: A => B): That
漂亮,清晰,明显它的作用。你可能想知道'那'是什么,如果你鼠标悬停或点击它可以扩展[B,那]文本突出显示'那',例如。
也许有一个小图标可以用于[]声明和(隐式...)块,所以它清楚有一点声明崩溃了吗?它难以使用令牌,但我会使用。现在......
def map.(f: A => B).: That
因此,默认情况下,类型系统的'noise'隐藏在人们需要查看的主要80%的位置 - 方法名称,参数类型和返回类型以简单明了的方式 - 几乎没有可扩展的链接如果你真的非常关心那么详细。
大多数人都在阅读scaladoc,以了解他们可以在类型上调用哪些方法以及可以传递哪些参数。我们有点过多的细节权利淹没用户如何恕我直言。
这是另一个例子......
def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]): PartialFunction[A1, B1]
现在,如果我们隐藏了泛型声明,那么它更容易阅读
def orElse(that: PartialFunction[A1, B1]): PartialFunction[A1, B1]
然后,如果人们将鼠标悬停在A1上,我们可以显示A1的声明为A1&lt;:A。仿制药中的协变和逆变类型也会增加很多噪音,这些噪音可以更容易地呈现给用户我想。
答案 11 :(得分:11)
我不知道如何打破它,但我拥有剑桥博士学位,而且我使用2.8就好了。
更严重的是,我几乎没有花时间使用2.7(它不会与我正在使用的Java库交互)并且在一个多月前开始使用Scala。我有一些Haskell的经验(不多),但是忽略了你担心的东西,并寻找与我的Java经验相匹配的方法(我用它来谋生)。
所以:我是一个“新用户”而且我没有被推迟 - 事实上它像Java一样让我有足够的信心忽略了我不理解的部分。
(但是,我之所以看到Scala,部分原因在于是否要将其推向工作状态,而我现在还不会这么做。让文档不那么令人生畏肯定会有所帮助,但让我感到惊讶的是多少它仍然在变化和发展(公平地说,最令我惊讶的是它有多棒,但变化紧随其后)。所以我想我所说的是我宁愿选择有限的资源投入让它进入最终状态 - 我认为他们很快就会期待这种流行。)
答案 12 :(得分:10)
根本不知道Scala,但几周前我读不懂Clojure。现在我可以阅读其中的大部分内容,但除了最简单的示例之外,还无法编写任何内容。我怀疑斯卡拉没有什么不同。根据你的学习方式,你需要一本好的书或课程。只需阅读上面的地图声明,我就可以或者 1/3。
我认为更大的问题不是这些语言的语法,而是采用和内化范例,使它们可以在日常生产代码中使用。对我来说,Java并不是C ++的巨大飞跃,这不是C的巨大飞跃,这不是Pascal,也不是Basic等的飞跃......但是像Clojure 这样的函数式语言的编码是强大的>一个巨大的飞跃(无论如何对我而言)。我想在Scala中你可以用Java风格或Scala风格编写代码。但是在Clojure中,你会创造一些混乱的东西,试图让你的命令式习惯远离Java。
答案 13 :(得分:7)
Scala有许多疯狂的功能(特别是涉及隐式参数的地方),看起来非常复杂和学术性,但旨在使事情易于使用。最有用的是获得语法糖(如[A <% B]
,这意味着类型A的对象具有对类型B的对象的隐式转换)以及对它们的作用的详细说明。但大多数情况下,作为这些库的客户端,您可以忽略隐式参数并信任它们做正确的事情。
答案 14 :(得分:6)
答案 15 :(得分:5)
似乎有必要在这里说明学位:B.A。 “政治学”和“计算机科学学士”。
重点:
这是否会让人们去Scala?
Scala很难,因为它的底层编程范式很难实现。功能编程让很多人感到害怕。可以在PHP中构建闭包,但人们很少这样做。所以不,不是这个签名,但如果他们没有特定的教育使他们重视潜在范式的力量,那么所有其他人都会放弃他们。
如果有这种教育,每个人都可以做到。去年我在斯卡拉与一群学校的孩子们一起制作了一台国际象棋电脑!他们有他们的问题,但他们最终做得很好。
如果你在商业上使用Scala,你是否担心这个?您是打算立即采用2.8还是等待看看会发生什么?
我不会担心。
答案 16 :(得分:4)
我也有牛津大学的数学学位!我花了一段时间来“获取”新的收藏品。但是我现在非常喜欢它。事实上,'map'的输入是我在2.7中遇到麻烦的第一件大事(也许是因为我做的第一件事就是将其中一个集合类作为子类)。
阅读Martin关于新2.8集合的论文确实有助于解释implicits的使用,但是文档本身肯定需要更好地解释核心API的方法签名中不同类型的暗示的作用。
我主要担心的是:什么时候2.8会被释放? bug报告什么时候会停止进入呢?让scala团队咬掉的东西超过他们可以咀嚼的2.8 /试图立刻改变太多?
我真的希望在添加任何其他新功能之前将2.8稳定发布作为优先事项,并且想知道(从旁观看时)是否可以对scala编译器的开发路线图进行一些改进管理。
答案 17 :(得分:-1)
使用网站中的错误消息怎么样?
那么用户何时需要将现有类型与适合DSL的自定义类型集成在一起。必须对关联,优先级,隐式转换,隐式参数,更高类型以及可能存在类型等问题进行良好的教育。
很高兴知道这很简单,但不一定足够。如果要设计广泛的图书馆,至少必须有一个人知道这些东西。