华为自研编程语言“仓颉”来了!鸿蒙应用开发新语言,性能优于 Java、Go、Swift
在刚刚召开的华为开发者大会(HDC 2024)上,华为内部研发已久的国产自主编程语言仓颉终于正式对外官宣!
随着万物互联以及智能时代的到来,软件的形态将发生巨大的变化。一方面,移动应用和移动互联网领域仍然强力驱动人机交互、设备协同、智能化、安全性等方向的创新,另一方面人工智能也同样驱动软件朝智能化、端边云协同等方向演进。新技术、新场景下应用软件的开发对编程语言提出了新的诉求和挑战。
仓颉编程语言作为一款面向全场景应用开发的现代编程语言,通过现代语言特性的集成、全方位的编译优化和运行时实现,以及开箱即用的 IDE 工具链支持,致力于为开发者打造友好开发体验和卓越程序性能。
当前在鸿蒙原生应用的开发中,支持声明式 UI 和一次开发、多端部署的 ArkTS 语言已经被广泛使用。仓颉语言作为面向未来的下一代编程语言,当前已经完成设计与实现并启动了开发者预览,未来将与 ArkTS 共同发展,为鸿蒙原生应用开发者提供极致高效的开发体验。
填补国产编程语言的空白
根据公开数据显示,中国开发者使用的编程语言主要是 Java、JavaScript、Python、HTML、SQL、C++ 等。
而这些主流编程语言均由国外厂商及科研机构把控。全球编程语言发展水平的高地主要集中在欧洲和美国。丹麦是其中一个代表,丹麦的奥尔胡斯大学一直有研究编程语言的传统,这里涌现出了像 Dart 的作者 Lars Bak 和 C++ 的作者 Bjarne Stroustrup 等人物。其他欧洲国家也有重要贡献,比如荷兰的 Guido van Rossum 开发了 Python,瑞士的 Niklaus Wirth 发明了 Pascal 和 Modula 系列,德国的 Martin Odersky 设计了 Scala。Kotlin 起源于俄罗斯的 099 语言,研究最初是苏联的科研机密项目,Kotlin 这个名字来自圣彼得堡附近的一个岛。此外,日本有 Ruby,巴西有 Lua,而中国几乎一片空白。
值得注意的是,在当前复杂的国际形势下,多数主流编程语言可能存在断供风险。比如,有相当多人在使用的 Java,实际上由 Oracle 主导,编译器 OpenJDK 等由美国公司强控制,风险较高。早前,谷歌在开发 Android 时未经授权使用了 37 个来自 Java 的 API,共涉及约 11,500 行代码,自此陷入跟 Oracle 长达十年的诉讼拉锯战,直到 2021 年美国法院才改判谷歌对 Java API 的使用属于合理使用。又如,Go 语言由谷歌开源,目前核心开发团队大多仍在谷歌,也存在一定风险。而由瑞士 ISO 委员会主导的 C/C++,相对而言风险较低。
此外,纵观业界头部大型公司,在编程语言方面均有部署,比如谷歌投入人力研发的 Go、Dart 等语言,苹果开源的 Swift 语言,核心都是为了打造生态入口、构建开发者生态。
考虑以上两方面因素,自研编程语言确有必要,但打造一门编程语言并不容易。计算机领域有许多学科分支,包括人工智能、计算机视觉、机器学习和自然语言处理等。除此之外,还有系统学科,涵盖计算机体系结构、网络、安全、数据库和编程语言。这其中,编程语言的研究人员非常少。例如,顶级会议 PLDI 每年只收录大约 77 篇论文,而人工智能领域的顶级会议则可以收录上千篇。
编程语言领域的课程难度极高,国内几乎没有相关书籍和课程,教材基本处于空白状态。这个领域主要学习的教材是《Types and Programming Languages》和《Practical Foundations for Programming Languages》,其中《Types and Programming Languages》中文版已经绝版。
从 1960 年代开始,编程语言作为计算机皇冠上的明珠,有 40% 图灵奖获得者来自编程语言学科。可见这个领域难度非常大,难度大得只要有一定的突破,就能够拿到图灵奖。
总的来说,设计和开发一门编程语言是一个复杂的系统性工程,需要长期投入。
拿 Java 举例来说,1991 年成立语言团队,到 1999 年虚拟机的发布,2006 年开源才进入语言成熟期,至今还在拓展应用领域、完善语言特性。
另一个例子是苹果的自研语言之路,早年乔布斯使用的是 Objective-C,2011 年因为性能安全、应用性的诉求启动了 Swift 的研发,Chris Lattner 以一己之力完成基本架构,之后带着团队完成语法设计、编译器、运行时、框架、IDE 和文档,4 年后才正式对外发布。而后,从正式推出到 2019 年发布 5.0 稳定版本,又历时 5 年。
构建编程语言周期长,挑战大,当前国内尚无规模商用的自研编程语言。国产编程语言本身也非常稀缺,仅有 Go 语言爱好者发起的凹语言、基于 Go 语言扩展而来 Go+ 语言、用中文来编写程序的易语言,以及目前比较热的 MoonBit。
正因为存在软件产业发展基础薄弱问题,工信部在十四五规划中将程序设计语言自主可控列为提升产业基础保障水平的一项重点工作。
基于构建开发者生态和编程语言自主可控的诉求,华为于 2019 年启动了仓颉编程语言的开发计划,经过五年不断打磨,完善语言和生态及其基础设施的完美度,如今终于正式对外公布。
仓颉编程语言的定位和竞争力
仓颉被设计为一款面向全场景应用开发的现代编程语言,主打高效编程、安全可靠、轻松并发、卓越性能、敏捷扩展。
在设计时,仓颉团队在安全性、易用性和性能之间进行了权衡。
设计语言时我们无法同时完美满足所有要求,例如,C、Rust、C++ 这类的系统编程语言性能极佳,但开发效率没那么高。JS 这类动态脚本语言开发效率高,但性能略差。Kotlin、Java、Go、Swift 这样的语言居于前两者之间,属于重业务开发的静态类型语言。
仓颉也选择了这种居中的定位,被设计为了具备自动内存管理功能、静态类型、面向应用开发的语言。在效率上,仓颉注重“语法简洁低噪音,且能面向领域易扩展”。在性能上,仓颉注重“垂直整合、性能可伸缩、稳定可预期”。在安全上,仓颉注重“缺省模式安全、强化编译期安全约束”。
目前 Android 和 iOS 操作系统都有首选语言。我们知道 Android 是一个由谷歌主导的适用于移动设备的开源操作系统,2019 年,谷歌宣布 Kotlin 成为 Android 应用程序开发者的首选语言。
而 Swift 则是 iOS 应用程序开发的首选语言。在移动操作系统领域,苹果公司面临着来自 Android 的激烈竞争。Swift 的出现降低了 iOS 开发的门槛,使更多开发者能够更容易地加入 iOS 生态系统。对于苹果公司而言,这意味着可以获得更庞大的开发者群体,从而推动 iOS 平台的繁荣发展。
这两种语言的定位和仓颉一样,居于动态脚本语言和系统编程语言之间。所以我们大胆推测仓颉也非常适合用于鸿蒙应用开发。
实际上,Swift 最初有着宏大的愿景,并不仅仅局限于 iOS 开发,它的创造者拉特纳曾在采访中说过,“我们一开始,就是要将 Swift 设计成为一门一统天下的语言。”这也是一门语言想要获得广泛流行的基本条件。包括谷歌开发 Go,也是希望向全世界提供一种通用的编程语言。仓颉的定位同样如此,据官方介绍,它是一款面向“全场景”智能的新一代编程语言,主打原生智能化、天生全场景、高性能、强安全。
-
原生智能化:仓颉编程语言内嵌了 AgentDSL 的编程框架,实现了自然语言与编程语言有机融合。多 Agent 协同,简化符号表达,模式可以自由组合,支持各类智能应用开发。
-
天生全场景:仓颉编程语言采用轻量化可缩放运行时和模块化分层设计,即使在资源受限的设备上也能流畅运行。同时,它支持全场景领域扩展,元编程和 eDSL 技术,可助力面向领域声明式开发。
-
高性能:仓颉编程语言采用全并发 GC,应用线程运行更加流畅,响应速度更快。轻量化线程设计进一步提升了并发性能,降低了开发成本。
-
强安全:仓颉编程语言将安全理念融入语言设计,帮助开发者专注于业务逻辑,减少安全漏洞的产生,实现“编码即安全”的愿景。
另外,仓颉编译器及运行时从全栈对编译进行优化,包括编译器前端基于 CHIR(Cangjie HighLevel IR)高层编译优化(比如语义感知的循环优化、语义感知的后端协同优化等),基于后端的编译优化(比如:SLP 向量化、Intrinsic 优化、InlineCache、过程间指针优化、Barrier 优化等),基于运行时的优化(比如轻量锁、分布式标记、并发 Tracing 优化等),一系列的优化让仓颉充分发挥处理器能力,为应用提供卓越的性能支持。
仓颉语言还对运行时进行原生的轻量化设计,通过对运行时模块化分层设计,定义仓颉公共对象模型和运行时公共基础组件,基于公共对象模型,实现运行时的内存管理、回栈、异常处理、跨语言调用等基础能力,大幅减少多个能力间的冗余对象设计,精简运行时体积。同时通过包的按需加载技术,减少仓颉应用启动的冗余包内存开销,因此对于资源敏感设备,占用资源更少,支持更友好。
在计算机语言基准测试 Benchmarks Game 上,仓颉相比业界同类语言取得了较为明显的性能优势。
另外值得一提的是,仓颉还支持面向应用开发的一系列工具链,包括语言服务(高亮、联想)、调试(跨语言调试、线程级可视化调试)、静态检查、性能分析、包管理、文档生成、Mock 工具、测试框架、覆盖率工具、Fuzz 工具以及智能辅助编程工具,进一步提升软件开发体验以及效率。
仓颉语言特性
目前,仓颉语言特性基本完备,可满足大多数开发场景。
作为一门多范式编程语言,仓颉支持函数式、命令式和面向对象等多种范式,包括值类型、类和接口、泛型、代数数据类型、模式匹配、以及高阶函数等特性。此外,仓颉还支持类型推断,能够降低开发者类型标注的负担;通过一系列简明高效的语法,能够减少冗余书写、提升开发效率;语言内置的各种语法糖和宏(macro)的能力,支持开发者基于仓颉快速开发领域专用语言(DSL),构建领域抽象。
下面我们从函数式编程、高效并发、跨语言互操作和原生智能化来具体了解仓颉语言的主要技术特色。(摘选自仓颉白皮书:https://developer.huawei.com/consumer/cn/doc/openharmony-cangjie/cj-wp-abstract,以及仓颉语言规约:https://developer.huawei.com/consumer/cn/doc/openharmony-cangjie/cj-lan-spec)
函数是一等公民
仓颉对过程式编程、面向对象编程和函数式编程都提供了良好的支持,包括但不限于值类型、类和接口、泛型、代数数据类型和模式匹配、以及函数作为一等公民等特性支持。
仓颉中函数可以作为普通表达式使用,可以作为参数传递,作为函数返回值,被保存在其他数据结构中,或者赋值给一个变量使用。
func f(x: Int) { return x } let a = f let square = {x: Int => x * x} // lambda 表达式 // 函数嵌套定义,以及函数作为返回值 func g(x: Int) { func h(){ return f(square(x)) } return h } func h(f: ()->Unit) { f() } let b = h(g(100))
高效并发:轻量化线程模型
仓颉线程采用的是 M:N 线程模型,因此本质上它是一种用户态的轻量级线程,支持抢占,且相比操作系统线程内存资源占用更小。
这种轻量化线程设计不仅降低系统的负担,而且使得开发者能够在不增加编程复杂度的前提下,轻松实现数千甚至数万个并发任务。其核心优势包括:
-
简单的并发编程:不对开发者编写并发代码做过多语法约束,使其方便地使用仓颉线程并专注业务处理。
-
轻量级的开销:由于创建和切换用户态线程的开销远远小于传统的内核线程,仓颉语言可以快速地创建和销毁大量用户态线程,使得开发高并发应用变得轻而易举。
-
更高的并发能力:仓颉语言通过用户态线程模型,可以实现非常高的并发数,这使得它特别适合于 I/O 密集型和高并发的网络服务场景。
-
减少上下文切换成本:在轻量化线程模型中,上下文切换发生在用户空间,避免了传统线程切换需要经过内核态和用户态之间频繁转换的高成本。在仓颉语言中,实现高效并发不再是一项复杂且耗时的任务。开发者可以通过简单的语法构造大量的用户态线程,无需担心传统并发模型中常见的性能瓶颈。假设我们有一个需求:需要同时处理多个网络请求。在仓颉语言中,这可以轻松实现,如下代码所示:
func fetch_data(url: String) { let response = http_get(url) process(response) } main() { let urls = ["http://example.com/data1", "http://example.com/data2", ...] let futures = ArrayList<Future<Unit>>() for (url in urls) { let fut = spawn { fetch_data(url) } // 创建仓颉线程进行网络请求 futures.append(fut) } for (fut in futures) { // 等待所有仓颉线程完成 fut.get() } }
在上述例子中,spawn 关键字用于创建一个新的仓颉线程,每个线程独立地执行 fetch_data 函数。仓颉语言的运行时环境会自动调度这些线程,而开发者只需关注业务逻辑的实现。最后通过获取线程结果来等待所有的仓颉线程完成,确保主线程能够同步地获取所有结果。
跨语言互操作
仓颉支持和 C、ArkTS/JS、Python 等编程语言的互操作,并采用便捷的声明式编程范式,可实现对其他语言库的高效复用和生态兼容。
跨语言调用的特性对实现“一套代码多端使用”的愿景非常有利,比如原来在嵌入式设备上用 C 语言开发,手表上用 JS 开发,手机上则用 Java,现在可以利用仓颉实现在跨设备、多语言的条件下做开发,并且三种语言在同一份代码里,还可以跨多个语言去做一些调试、定位,甚至是做一些 UI 和代码区之间的双向预览。
以 C 语言互操作为例,因为 C 语言太容易造成不安全,所以仓颉规定所有和 C 语言互操作的功能都只能发生在 unsafe 上下文中。unsafe 上下文是用 unsafe 关键字引入的。unsafe 关键字可以修饰一个代码块(unsafe 表达式的类型就是这个代码块的类型),也可以修饰一个函数。
语法定义为:
unsafeExpression : 'unsafe' '{' expressionOrDeclarations '}' ; unsafeFunction : 'unsafe' functionDefinition ;
另外,仓颉编程语言要调用 C 函数,需要先在仓颉代码中声明这个函数且用 foreign 关键字修饰。
foreign func foo(): Unit foreign var a: Int32 = 0 // compiler error foreign func bar(): Unit { // compiler error return }
仓颉通过调试器 cjdb 提供源码级调试能力,支持跨语言调试,比如单步进入 / 退出跨语言函数代码、跨语言下的完整调用栈查看,一个调试器完成多种语言的调试,同时支持对仓颉线程进行调试,最大程度上提升用户调试体验。
仓颉 C 跨语言调用示例:
仓颉 C 跨语言单步进入:
仓颉 C 跨语言调用栈,FFI-C 函数内单步调试:
原生智能化
从计算机与软件演进历程看,伴随硬件及应用的革新,语言每十年经历一次大变革。我们也经历了几个时期:PC 服务器隐藏硬件细节的高级语言如 Lisp、C、C++,传统互联网跨平台
易移植的语言如 Java、JS、Python,移动互联网降低开发门槛注重开发体验的语言如 Swift、Kotlin。现在,我们到了一个智能万物互联时期,需要一个能适应全场景的 AI 原生语言。
虽然 AI 技术已被广泛普及和应用,但 AI 应用开发通常需要开发者具备较深的专业知识,并且面临一定的挑战,例如,学习曲线陡峭、集成复杂性等。
华为设想通过仓颉语言提供原生 AI 能力来简化开发难度,这意味着 AI 相关的功能,如模型部署、智能决策等,将成为语言表达力的一部分,这将带来高效的开发体验。
常规的 AI 赋能是通过提供 AI 应用框架来实现,但是如果能在语言原生能力上提供更简洁的语法表达来降低开发者编写 AI 应用的门槛,这会是一件非常酷的事情。因此仓颉借鉴 web 端和移动端的技术发展,希望通过领域特定语言 DSL 能力来构建类似 AI 领域的声明式范式。
Agent DSL 是仓颉团队现在正在畅想和尝试的 AI 原生能力,它是一种专为 AI Agent 开发和多 Agent 协同而设计的领域特定语言,是一种内嵌在仓颉语言中的 DSL(即 eDSL),开发者无需额外学习复杂的库和框架,通过 DSL 可以简单直观地使用 AI 功能。
// Agent的定义 @agent class Planner { @prompt[pattern=APE] ( action: "帮助用户制定旅行路线", purpose: "让用户在计划时间内多参观景点并得到充分休息", expectation: “生成一条合理的旅游景点路线,包括时间、景点、通勤等信息" ) } // Agent的使用 let agent = Planner() let result = agent.chat("我想前往上海")
从代码段不难看出,在仓颉语言中对于 Agent 的声明和使用语法与仓颉本身语法一致,既能享受仓颉的静态检查能力,又不会给开发者带来额外的学习负担,将高效编程、安全可靠发挥到极致。
Agent DSL 不仅能提升 AI 应用开发的效率,还能使代码更为精准地对应 AI Agent 的操作、决策过程。整体设计希望能达成如下效果:
-
高级抽象:Agent 作为 DSL 中的内置语言抽象,其定义和描述更加自然直观、易于理解和维护。
-
极简多 Agent 协同编程:通过流式符号抽象出不同的 Agent 协同模式,开发者可以轻松地利用多 Agent 协作来开发智能化程度更高的应用。
-
智能化开发工具链:基于 Agent DSL,工具链为开发人员提供从应用开发到性能调测、调优的全方位智能支持。
除了 Agent DSL,原生 AI 应用框架也是仓颉语言团队正在构建的能力,通过语言原生以及框架的配合给开发者带来全场景智能化时代的应用编程新体验。
本文文字及图片出自 InfoQ
共有 1 条讨论