翻译:顾远山
版权归作者所有,转载请标明出处。
原文链接:Why F# is the best enterprise language
原文作者:Scott Wlaschin
此贴为《2018 F# Advent Calendar》其中的一篇,其他帖子请点击链接围观,特别感谢Sergey Tihon大神组织了这次活动。
“为什么说F#是最好的企业(编程)语言”,取这样一个标题,并不是为了骗点击率,而是发自我内心的想法,我会尝试通过此贴证明这一点,如果你能坚持看完,我希望你也会同意我,或者至少有那么一点点被说服。来吧!
需要明确的是,我只会讨论所谓的“企业开发”。我并没有宣称F#对系统编程、游戏开发或兴趣项目等都是最好的选择。针对某种开发目标的方法也许不适用于另一种,正如彼之蜜糖,吾之毒药。企业开发有着自己特定的约束条件和诉求,所以我才觉得F#特别合适。
我得从一条重要的告诫开始,那就是——我不认为某个项目的成败取决于使用某种特定的编程语言,相比编程语言而言更重要的,是诸如顺畅的沟通、清晰的需求、重视用户体验、靠谱的期望等良好实践。要是编程语言真有那么重要,那世界上就不会有靠PHP、VB或JS成功的公司,所有的职位都只需要会Haskell和Lisp就可以了。
但我还是觉得编程语言的选择可以影响生产效率、可维护性和稳定性,这正是这篇帖子接下来要谈及的内容。其实要证明诸如“F#是最好的企业编程语言”这种断言非常简单,我要做的无非就从“企业软件项目纵向研究之若干”* 中挑一个,再不然就从众多集结了大量老码农的受控试验中挑一个。哈哈哈,当然,没这回事。对于一个动辄万亿美元级别的行业,我们通常只靠秘闻野史、老掉牙的神话和直觉来做决策,要传出去了会非常骇人听闻了。** 所以我没有任何实质的证据,哎,但我会至少尝试呈现一份有理据的论证。你要是同意我的前提,我希望你至少也认真对待我的推论。
**没错,我知道“不好使网站”和“烂证据网站”,但我坚持自己的观点。
*译者注:此处作者引用了Kevlin Henney的一条Twitter。“根据我的经验…”可以翻译为:基于一项纵向的、独立观察的、没有正式文档记录的、样本数量为1且不受控的研究…原文为 "In my experience..."Translation: Based on a longitudinal, self-observed, undocumented study with N=1 and without a control...
企业开发的特征
那么“企业”开发都有什么关键特征呢?
软件开发并非业务焦点
在本贴谈论的“企业”里,软件通常被当成工具对待,它是个成本中心而非利润中心,用最新的技术或者请最好的码农,或(难过地)投资员工技能培训,是没有压力的。这意味着这里提到的“企业”跟业务规模无关,根据我的定义,Google不做企业开发,而一家50人左右的规模的B2B公司很可能会做企业开发。这同样意味着开发内部软件去赢得竞争优势的诸如FinTech等公司,也不算在此贴所说的“企业”的范围内。
项目以业务为中心而不是以技术为中心
企业开发的目标通常是支持业务流程而不是实现一套具体的技术需求。最入门程度的典型企业软件仅仅处理数据移动和数据转换,这听起来很琐碎,甚至经常被人鄙视并非“真正意义上的编程”。但业务流程有人介入,而且随时有人介入,这让情况永远都很复杂。实现高效的映射归约算法或优化图形着色器也许有点棘手,但很可能业务流程的处理也同样棘手!这段30年前关于COBOL的引用就很好地概括了这一点:
编程语言教科书中明确指出对问题域的偏好,提到COBOL可以有做业务数据处理的方向…其中能被处理的问题是…相对简单的算法外加大量的输入输出,如为大型组织计算工资单。
实际上任何一个写过正经算工资单程序的同学都很难说它“相对简单”。我相信计算机科学家并不清楚太多业务数据处理任务的复杂性,也有可能他们发现很难为现实中数据处理应用涉及的恼人却普遍的复杂性提供优雅的理论索性放弃了。
可悲的是,企业开发从来不曾性感过。
企业项目通常持续较长时间
当然这对企业开发来说并不独特,但企业软件项目能活很长时间(如果他们有童年)倒是挺常见的。很多项目持续五年甚至更长——我自己就熟悉一个上个世纪七十年代就开始的项目——在项目整个生命期,会有很多码农参与其中。于是得出两个推论:
项目生命周期的大都花在了所谓的“运维”上。“运维”是个误导性的术语,它主要指的是低速演进(偶尔也会有高速恐慌)。
如果你是一位码农,负责某个已经存活了很久的项目,那这个项目绝大部分代码都不会是你写的,也不是你现在这个团队中任何一位同学的手笔。
罗伯特斯莫夏尔曾经有个非常有趣的演讲,当中提到他模拟不同规模的团队在不同时期生成代码的过程,比如当前的团队在五年后普遍只会贡献37%的代码。
模拟一个七人团队五年的代码贡献情况团队越大,项目持续时间越长,贡献的代码百分比可以降得更低。
模拟一个大团队长期项目的代码贡献情况没错,这些都是模拟,但“根据我的经验”,它们是对的。
企业项目经理对风险容忍度较低
基于所有这些因素,项目经理厌恶风险并鲜有尝新——为什么要打破原有已经用得很好的一套?俗话说“过程就是组织的疤痕组织”,稳定性远比效率重要。然而,新的环境条件偶尔出现,强行导致业务变化,有些甚至发生在极其保守派的业务。比如上个世纪九十年代新兴的“内网”和“因特网”吓坏了好多人,它们跟后来崛起的Java和VB/ActiveX息息相关,以下是当时媒体的一些炒作:
1996:由于网景和微软在争夺网络霸权,所以Java和ActiveX都是董事会的关键部分。1997:Java之前世界上没有互联网编程语言。
这些文章发布了不到十年,主流企业编程语言的而且确变成了Java和C#。得益于移动端应用程序的广泛应用和云技术的崛起,我想大家已经走到了另一个纪元。现在企业也愿意承担风险尝试新技术防止落伍,当然挑战在于采用新技术的同时不会产生大混乱。
选择一门企业编程语言时,有哪些重要的方面需要考虑?
从项目经理的角度出发,所有这些因素如何影响你对编程语言及其生态系统呢?
它应当对企业友好
项目经理不仅需要选择编程语言,他们还得保证周边的生态系统,以及对其进行后续支持。如上所述,企业开发所用的技术栈并不处于最前沿。相反,如果生态系统能得到企业友好型公司如微软,甲骨文或谷歌等的支持,必将是一大优势。
此外,从企业管理者的角度来看,编程语言及其生态系统对企业服务的深度支持是至关重要,包括但不限于企业数据库(Oracle,Sql Server),企业Web服务器,企业身份验证(AD,LDAP),企业数据格式(XML)等。对最新热点的支持不大可能是他们最关心的问题。
它应当面向未来
鉴于企业项目较长的存活时间,我们希望确保生态系统和工具在未来仍然能到支持和支持,比如十年内。就算新平台出现,你也不必抛弃现有的全部代码。
它应当相对灵活
如果你打算为企业搭建一套生态系统,理想情况下,您希望在尽可能多的不同情况下使用它(例如桌面应用程序,服务器应用程序,Web应用程序)和不同的目标平台(Windows,Mac,Linux,移动设备等)。
它应当便于维护
由于团队成员很可能在项目的整个生命周期内进行轮换,并且大多数代码都不会由当前团队编写,因此主要需要关注的问题包括:
理解难度:看懂上一个团队成员编写的代码有多难?
生产效率:我们能否快速安全地添加新功能?
安全性:我们是否有信心做更改或重构不会弄坏任何东西?
选择一门企业编程语言(之一)
有了这些要求,我们可以用它们对企业编程语言的选择进行精简。
为了易维护性和安全性,大多数人都同意你需要一门静态类型的语言。当你有个几十人到几百人一同协作的大型代码库且要用上很多年的情况下,静态类型语言支持更好的重构,编译时错误可以帮助防止错误的代码投入生产。所以,PHP,Python,JavaScript和Clojure,你只能对它们说抱歉了!以下是约翰卡马克关于此话题的说法:
最佳实践只是美好的理想,现实操作起来它们并不管用。有些东西如果在语法上可以被输错,那么它最终就会被输错。这是我非常重视静态分析的原因之一,我希望能够启用更严格的语言子集来限制码农,因为我们经常犯错。
软件开发不是业务的重点,言下之意重点是稳定性和生产效率,而不是性能。这意味着企业编程语言不应允许具有潜在危险的操作,诸如控制内存和指针运算。即使它可以被安全地进行,如在Rust和现代C++中,付出的代价太大,和挤出来有限的额外性能相比,并不值得。与其这样,不如让垃圾收集器处理所有事情,还能节省时间专注于其他事情。
它应当对企业友好,所以无疑大家最喜欢的是:
Java(和JVM上可以搭载Java生态系统的语言)。
C#(和.NET生态系统中的其他语言)。
Go在这里也获得了一些分数,得益于Google的支持,你可以相信那些关键的企业库还是可用的。
所以,没有什么惊喜,意料之中的结果,Java和C#。
如果这是2008年,我们就到此结束了。但今年不是2008年,我们也不是2008年的我们。在过去十年中,世界上涌现了大量新编程语言,相比C#和Java,其中有些语言是企业编程语言更有力的竞争者。我们来看看原因是什么。
函数式编程的兴起
函数式编程现在是新热点,但无论炒作如何,大多数现代编程语言都引入了对函数式编程友好的功能,而这些功能为软件质量带来了很大的变化:
高阶函数在很多情况下取代了重量级接口(没有高阶函数,C#的LINQ和Java的Streams库都不可能存在了)。
不同的默认值,例如默认的不可变性和默认的非空值,有了这些,运维和代码理解容易多了,因为在代码一旦与默认值存在偏差差,码农们会被明确地告知。
显化副作用 一直被函数式编程社区所强调,包括诸如显式错误处理的结果类型、把输入输出及其他非纯粹性源放到应用的边缘(常见于核心功能/命令式操作和洋葱架构方法)。***
***译者注:在函数式编程中两种代码:一种是纯函数式代码,一般用于纯计算;另一种是副作用代码,用于与外部的交互如磁盘IO、网络访问、屏幕显示等。
最后,最重要的是,受FP影响的语言具有代数化的数据类型。 也就是说,不仅是记录/结构体,还有“选择”类型(即总和类型或可区分联合)。 在我看来,这些对高效的领域建模至关重要。 我必须这么说,因为我写了一本关于这个主题的书,但有这看法的不止我一个人。
如果我们看一下支持这些功能的语言,最终得到的有主流的静态类型函数式编程语言(Haskell,F#,OCaml)和更现代的受函数式编程影响的语言:Swift,Scala,Kotlin,Rust,TypeScript等。
正如我上面所说,如果诸如无服务器等新技术能提供竞争优势(我认为它们可以),再加上新旧技术切换时能做到最少的混乱(取决于语言的选择),那这些新技术的兴起意味着企业愿意转向这些受函数式编程影响的语言。
抽象过多的危险
有些FP语言(特别是Haskell和Scala)支持一些允许高级抽象的功能。有些人喜欢在这里引用Dijkstra:
“抽象的目的不是模糊,而是创造一个新的语义层面,其中一个可以绝对精确”
- E.W. Dijkstra
这很赞,但我相信在企业开发的特定环境中,过多的抽象可能会导致问题。如果使用抽象过于自由,则需要项目里所有的码农对“新语义级别”有相同的理解,这对培训和就业能力是种负担。最后变成只是某一个人在编码过程中用范畴论获得过多乐趣,但代码对于其他人却不可维护。
诚然你可以用低级功能把事情搞砸,也可以用高级功能把事情搞砸。对于一门企业编程语言,我们需要对编程语言的能力掐头去尾,再尽可能鼓励码农用“唯一的方法搞定”。**
因此,我在这里要处罚Haskell和Scala,因为它太容易滥用。
**人们喜欢用Go或Elm作为编程语言的原因之一,是因为它们是限制性的。用一种标准的方式做事,反过来就意味着阅读和维护别人的代码是非常直观的。
但是多少的抽象算过多?
泛型是否太先进了?十五年前或许是的,但今天明显它已是主流特性了。(Golang设计师不同意!)但是Lambda怎样?Monads又怎样?我认为大多数函数式编程的概念现在都处于主流的边缘,并且十年后时间将被普遍接受,因此拥有一种支持它们的语言并非没有道理。
对我而言,在2018年,“恰到好处”的抽象层次是在诸如OCaml和F#等ML语言中找到的。 在接下来的10年时间里,情况的发展也许会有所不同,我们没准能够向上调整一下可接受的抽象层次。然而,由于员工技能的差异,我不信更抽象的数学风格编程(诸如Idris,Coq)在企业中将是常见的。没错,这可以通过更好的培训或经过认证的软件工程师资格来解决,目测我等不到这一天了。
选择一门企业编程语言(之二)
如果我们按照上面的“企业”标准过滤这些较新的语言,我们最终会得到支持.NET和JVM的受FP影响的语言,即:
.NET上的F#;
JVM上的Kotlin;
我也将添加TypeScript,因为它很好地支持并符合“企业”标准。
再来总结一下“为什么不是X语言”我所持的反对理由:
C#和Java - 这两个没问题,但F#和Kotlin支持更好的默认值(不可变性)、更好的副作用以及代数式的数据类型。
Swift - 它在Apple生态系统中得到了很好的支持,但没有任何迹象表明它一般火到企业中。
Ceylon - Kotlin有更多的动力。
Haskell - 是的,Haskell确实严格执行纯度,这很赞,但这不是编程的唯一组成部分。更重要的是,没有逐渐迁移到Haskell的路径 - 你直接就被抛到了深处。这可能对学习函数式编程很有用,但不适合企业,在我看来。
Scala - 我恐怕可以用太多不同的方式做事是劣势。 Kotlin更加企业友好。
OCaml - 如果你不需要企业支持,那么OCaml是一个很好的选择。否则F#会更适用。
Go - 非常适合某些事情但不建议企业使用,因为对类型的域建模的支持很弱。
Elm/ Purescript - 目前只支持前端。
Reason ML - 目前只支持前端。另外,为什么不使用OCaml?
C ++ / Rust - 如果你不需要性能,GC系语言更容易使用。
Erlang / Elixir - 非常适合高并发,但不适合企业。
PHP / Python / Ruby / etc - 我非常喜欢Python,但是当你有超过10K的LoC时,可维护性就会消失。如上所述,静态类型语言是大型项目的唯一途径。
高等级类型呢?类型类呢?GADT呢?
哦亲爱的,三位决赛选手中没有一位现在能支持这些,我会让你判断这些是否真的是企业发展的突破口。
挑选一个喜欢的
剩下的三种语言(F#,Kotlin和TypeScript)都是不错的选择,它们都是开源,跨平台和企业友好的。如果您已经在使用JVM,那么显然Kotlin提供了最佳的迁移路径。类似地,如果你在后端使用Node,那么TypeScript是好的(虽然信任npm包可能是个问题)。但是如果你正在进行绿地开发(或者如果你已经在.NET上),我相信F#有优势(可能是我有点偏见…)
它对非正式领域建模有出色的支持。
它是函数优先的,使用函数极其组合作为主要的开发方法。
不可变性真的无处不在 - 代数式数据类型和集合的默认就是不可变的。
它具有广泛的功能。例如:
你可以构建支持数百万客户的微服务。参考jet.com如何做到的。
你可以使用Fable在F#中编写JavaScript。例如VS Code的F#插件名为Ionide,是用F#构建的,并以这种方式转换为JS。
你可以使用SAFE Stack或WebSharper开发全栈式Web应用程序(在前端和后端之间共享代码)。
你可以使用类似Elm的方法使用Fabulous库构建移动应用程序。
你可以使用Avalonia构建XAML或WinForms桌面应用程序。
你可以创建轻量级脚本,例如构建和部署管道。该视频展示了使用自己的脚本编写的好处,而不是像VSTS/Octopus那样被锁定在供应商的工具中。
脚本的另一个不错的用途是浏览器UI测试。
当然你也可以做数据科学。
当然Kotlin可以做上述其中一些而TypeScript可以做上述其中另一些,但我认为F#总体上用途最广泛。
至此,这便是我的结论!请随意在评论区表达不同意!
顺便说一下,如果你有兴趣了解更多关于F#的信息,请查看2018 F# Advent Calendar的其余部分,如果你喜欢看视频,这里有一些很好的例子,足以证明F#的多功能性:
Domain Modeling Made Functional
Build your own Excel 365 in an hour with F#
Exploring StackOverflow Data with F#
Live programming of the SAFE F# web stack
如果你对通过函数式方法进行领域建模和设计,这里有我写的一本书,《函数式实现领域建模》。这是一本初学者友好的入门书,涵盖了领域驱动设计,类型建模和函数式编程。
Domain Modeling Made Functional作者:Scott.W; 发现有错?请报错;关注作者的Twitter。
译者后记:这是我正经翻译的第一篇文章,花了大约十个小时。尽管本人一直从事企业系统项目工作,使用函数式编程也超过十年,但原文作者功力深厚,用词随心,翻译时还是尽可能多参考资料,唯恐错漏。如有不当的地方,还望大家多多指正,共同进步。
网友评论