Software Intelligence的时代已经来临!
    伞源科技相关问题

    今天介绍个有趣的项目semantic,当然也跟我最近的工作有直接关系(Apaas平台-lowcode,nocode). 多语言解析一直是软件辅助编程的一个难点,需要同时支持多种语言的在线编译,解析和分析不是一个简单的问题。业界通用做法是通过AST和REPL方式来提供支持,当然当前的FAAS中的虚拟机多语言环境预封装我也归类为REPL解决方案的一种。

    今天介绍semantic的一个开篇,why-haskell,说明该工具选择haskell语言的原因。有一句话不吐不快,当下很多程序员都自称自己为java程序员,golang程序员,我只能一笑置之。比如golang语言所谓最大的特性CSP并发模型在erlang,hasckell等语言中早就支持而不是什么新的技术。在我看来程序员首先是工程师,应该面对某一个领域解决问题而不是自己标签化为某一类语言的程序员-话说那些程序语言也不是他们写的。了解语言的特性很难么?任何一个合格的程序员应该在一周内拿起任意一门通用语言来进行软件开发。当然我也不是所谓的语言门徒,就认定Haskell无敌,虽然这门语言的能力非常强大,分布式系统,形式化证明,人工智能,WEB开发都能做。我这里仅仅是想跟大家分享下semantic工具开发团队的选择倾向和目的。

    什么是Haskell?
    Haskell是一种标准的、通用的、编译的、纯函数式的编程语言,具有非严格的语义和强静态类型(这个是重点之一)。我们使用久负盛名的Glasgow-Haskell编译器(GHC)和构建系统cabal,在内部也使用了Stack。开头的句子带来的信息足够密集,你可以花很多时间研究“纯功能”、“非严格语义”和“强静态类型”之类的东西的历史和优点。我们将把它作为一个练习留给读者,而将重点放在我们在语义上非常依赖的语言的几个有趣的方面。

    为什么是Haskell
    或者更具体地说,为什么语义是用Haskell写的?
    语义项目涉及解析、分析(评估)和比较源代码,因此我们牢牢扎根于编程语言理论(PLT)的学术领域,并花费大量时间将现有研究应用于分析GitHub源代码的实际问题。Haskell非常适合这个领域。它的语言特性允许简洁、正确和优雅地表达我们使用的数据结构和算法。该语言使我们能够构造编程语言语法项和diff的代数表示,其中我们将支持的每种编程语言表示为语法项的开放联合。
    Haskell的许多方面使得一个项目像语义上的可行一样雄心勃勃:强大的类型、惰性求值和纯粹特性,仅举几个例子。然而,Haskell是必不可少的,有一个单独的原因:它支持丰富的、用户定义的控制流。

    控制流
    在像Go和Java这样的语言中,控制流的细节被嵌入到语言中,就像所有ALGOL子代一样,程序的执行从main()开始,自上而下通过函数和方法调用继续,直到程序进入无限循环或终止。

    Haskell是不同的。在Haskell中,控制流不是由语言决定的,而是由使用的数据结构决定的。相同的语法用于不确定性和回溯计算、并发性和并行性以及传统的命令块:用户定义的解释函数(而不是内置的语言语义)决定代码的执行方式。这几乎不可能在像Go这样的语言中实现,因为它对抽象和多态性的支持有限,而且在Java中是一个维护噩梦:我们的20k行代码中的每一行都需要重写为数据结构而不是函数。在其他语言中,这根本不是一项现实的任务;甚至像OCaml和Swift这样的函数式语言也缺乏这种抽象级别。

    这方面的一个例子是可恢复异常的概念。在语义的解释过程中,根据过程的调用上下文识别和处理无效代码(未绑定变量、类型错误、无限递归)。因为语义在其核心是一个解释器,所以这个特性对于快速开发是必不可少的:我们在构造解释和分析过程时,通过专门化我们的调用站点,指定GHC在执行不可信的、可能无效的代码时应该如何处理错误。将其移植到Java需要极大地滥用try/catch/finally机制,因为Java无法分离控制流的策略和机制。而且考虑到Go没有例外,这样的功能是完全不可能的。

    运行时正确性
    讨论强类型的优点时,已经写了很多内容。虽然对这些优点的全面探索超出了本文的讨论范围,但值得一提的是,语义作为一种规则,不会遇到运行时崩溃:空指针异常、缺少方法异常和无效的强制转换都是完全避免的,因为Haskell几乎不可能构建包含此类错误的程序。虽然类型安全级别不足以确保所有程序的正确性,但语义代码团队将大部分时间花在功能上而不是调试生产崩溃这一事实确实非常显著,这在很大程度上可以归因于我们对语言的选择

    研究
    语义是一个独特的项目,我们经常发现自己处于现代计算机科学研究的边缘。因此,我们没有可以建立的历史:我们灵感的外部来源和抽象的飞跃来自研究和学术界。举一个例子,我们很可能是最大程度地利用了Oleg Kiselyov关于可扩展效应的工作,也是业界第一个使用Wu等人的高阶效应概念的团队。
    这项关键研究不是在java或C++中完成的,而是在Haskell中:简洁、力量和关注正确性,使研究人员关注问题的本质,而不是将高级研究与传统语言相结合的繁重任务。用Haskell编写可以让我们在他人工作的基础上进行构建,而不是陷入阅读、移植和错误修复的循环中。

    工业界
    GHC是一项具有25年以上历史的坚实技术。Haskell语言和GHC作为主要的实现,都有积极的、有效的委员会来指导语言的未来。Haskell被广泛应用于工业界、Facebook和其他公司。

    我们对语言的体验
    Haskell既有效率又能让人大开眼界。尽管如此,它也有它的弱点,让我们来谈谈这些。
    编辑器工具还不够标准(特别是与Java和C#等语言社区相比),而且很挑剔——我们最终往往只是在一个单独的终端中编译。
    类型系统的边界。我们经常发现自己在Haskell令人难以置信的类型系统的边缘工作,希望依赖类型或达到复杂的工作区,如先进的重叠技术设计的奥列格基斯利约夫和西蒙佩顿琼斯。
    基础胶水特性。Haskell在运行web服务器等标准基础设施功能方面非常有能力,但它并不是语言社区关注的焦点,因此当您需要插入到现代基础设施时,常常需要编写自己的库和组件。
    惰性求值并不总是你想要的,而且可能会有性能问题,并使一些调试活动令人难以置信地沮丧。我们使用StrictData语言扩展来解决这些困难。
    haskell以难学著称。其中一些是当之无愧的,但其中一半与我们中有多少人第一次学习命令式编程有关,而转换到函数式范式需要一些耐心。Haskell还利用了一组更严格的抽象概念,这些抽象概念对web开发人员来说可能并不熟悉。然而,我们有非常好的运气,在新的团队成员与广泛的经验和学习哈斯克尔资源的质量真的提高了。
    在这一点上,我们非常重视Haskell的语言特性,以实现本项目的许多目标:抽象解释、图形分析、效果分析、代码编写、AST匹配等。您能用另一种编程语言实现语义吗?当然。项目的语义差异部分的早期原型是在Swift中完成的,但它很快变得笨拙,甚至第一个粗糙的Haskell原型的性能也大大提高。自从采用Haskell之后,我们就可以轻松地插入GitHub的其他基础设施:作为命令行工具、web服务器(HTTP/JSON)和Twirp RPC服务器运行。我们是Kubernetes和Moda的早期采用者,现在是GitHub的gRPC Twirp,经常在这些新的基础设施组件上发布我们的应用程序,远远领先于其他团队。我们已经管理了自己的构建系统,很快采用了Docker等新技术,并在项目的短生命周期内进行了大量的工作。我们还没有受到语言选择的限制。如果有什么不同的话,我们每天都会惊讶于Semantic在保持强大的静态类型系统的所有优点的同时,能够抽象和表示六种(和计数)编程语言的语法和求值语义。如果我们选择了一种更“流行”的语言,很可能会陷入数十万行代码的泥潭,抱怨我们的技术债务、应用程序性能以及添加更多语言的负担。现在,我们已经拥有了2万行Haskell代码和一些难以置信的程序分析功能,不用担心添加更多的语言或支持GitHub不断变化的需求。