结构化编程语言-Dart
2013-12-16 14:19:39 阿炯

Dart是一种基于类的可选类型化编程语言,设计用于创建Web应用程序,面向对象、带垃圾收集的类 C 语言。由Google主导开发,于2011年10月公开。Google称Dart的设计目标是为Web编程创造结构化但又富有灵活性的语言;编程方法一目了然,符合程序员的自然习惯,易于学习,能在所有浏览器和不同环境中实现高性能。它的开发团队由Google Chrome浏览器V8引擎团队的领导者拉尔斯·巴克主持,目标在于成为下一代结构化Web开发语言,类似JavaScript。Dart也是一种面向对象语言,但是它采用基于类编程,只允许单一继承,语法风格接近C语言。采用BSD协议授权。维基百科对其的定义是:“Dart 是谷歌开发的一种通用编程语言,后被 Ecma 认定为标准(ECMA-408),被用于构建 Web、服务器、桌面和移动应用程序。”


Dart代码以两种方式执行,一种是原生虚拟机,一种是JavaScript引擎,用编译器将Dart代码翻译成JavaScript代码。这允许程序员在Dart中创建Web应用,编译后在任何浏览器上运行。Dart语言官网提供了名叫Dartboard的在线应用,让感兴趣的开发者在线上编程和运行。ECMA已经成立技术委员会TC52展开标准化Dart的工作,同时由于Dart能够被编译成标准的JavaScript,它能够有效地在所有现代浏览器上运行。2014年7月,ECMA的第107次全体大会通过了第一版Dart语言规范。

在2011年10月10日的 GOTO 大会上,谷歌的两位工程师发布了“Dart”,旨在帮助开发者构建 Web 应用程序。1.0于2013年11月14日发布,曾经踌躇满志地想要取代 JavaScript 成为 Web 开发的首选语言;然而不久后,谷歌放弃了这个想法。因为 Dart 不仅仅在语言本身,包括它的整个生态圈,都比 JavaScript 落后一大截。Dart 开发团队转而求其次,在可以被编译成 JavaScript 的同时,Dart 在 2.0 版本中也针对 Web 和移动客户端开发进行了独特优化,从而能让它更好地满足客户端开发人员的需求;而在谷歌内部, AdWords、AdSense 和 Fiber 项目团队都把 Dart 融入其前端应用开发。而随着跨平台移动 UI 框架 Flutter 的走红,作为 Flutter 的御用开发语言,Dart 也一时名声大噪。多年来已经有不少其他公司用 Dart 来构建自己的关键性应用程序。

示例代码:
class Point {
 Point(this.x, this.y);
 distanceTo(Point other) {
  var dx = x - other.x;
  var dy = y - other.y;
  return Math.sqrt(dx * dx + dy * dy);
 }
 var x, y;
}

main() {
 Point p = new Point(2, 3);
 Point q = new Point(3, 4);
 print('distance from p to q = ${p.distanceTo(q)}');
}

根据 Stack Overflow 开展的 2022 年开发者调查显示,Dart 目前在编程语言领域的流行度排名第 16 位。在近 72000 名受访者中约有 6.54% 正在使用 Dart;相比之下,9.16% 的受访者正在使用 Kotlin(第 15 位),9.32% 在使用 Rust(第 14 名),11.15% 的人在使用 Go(第 13 名)。苹果的 Swift 编程语言排在第 19 位,占比 4.91%。在全部受访者当中,应用最广的编程语言是 JavaScript,这位冠军的覆盖率达到了惊人的 65.36%。

Flutter

Flutter是一个由谷歌开发的开源移动应用软件开发工具包,用于为Android、iOS、Windows、macOS、Linux Desktop、Google Fuchsia开发应用,使用Dart语言开发也同在BSD协议下授权使用。

Flutter第一个版本支持Android操作系统,开发代号称作“Sky”。 它于2015年4月的Flutter开发者会议上被公布,宣称其目标为实现120FPS的渲染性能。在上海Google Developer Days的主题演讲中,Google宣布了Flutter Release Preview 2,这是Flutter 1.0之前的最后一个重要版本。2018年12月4日,Flutter 1.0在Flutter Live活动中发布,是该框架的第一个“稳定”版本。2019年12月11日,在Flutter Interactive活动上发布了Flutter 1.12,宣布Flutter是第一个为环境计算设计的UI平台。2022年5月12日,在 Google I/O 2022 发布了 Flutter 3,正式支持了 Windows、macOS、Linux 等操作系统。

主要组成部分包括:
Dart平台
Flutter引擎
基础库
定制化设计语言的组件
Flutter DevTools
    
Dart平台
Flutter是使用Dart语言编写,并利用该语言的许多高级功能。在Windows、macOS和Linux上,Flutter在Dart虚拟机中运行,该虚拟机具有即时编译执行引擎。在编写和调试应用时,Flutter使用即时编译功能进行“热重载”(Hot Reload),可以将对源文件的修改注入正在运行的应用中。Flutter通过支持有状态的热重载来扩展此功能,在大多数情况下,对源代码的更改可以立即在运行的应用中反映出来,而无需重新启动或丢失任何状态。Flutter应用的发布版本在Android和iOS上都进行了提前(AOT, Ahead Of Time)编译,使Flutter在移动设备上可以高性能地运行。

Flutter 引擎
Flutter的引擎主要使用C++开发,通过Google的Skia图形库提供底层渲染支持,亦提供平台相关的SDK,例如Android和iOS。Flutter引擎是用于托管Flutter应用的可移植的运行环境。它实现了Flutter的核心库,包括动画和图形、文件和网络I/O、可访问性支持、插件架构以及Dart运行环境和编译工具链。大多数开发人员将通过Flutter框架与Flutter进行交互,该框架提供了一个现代、响应式的框架,以及一组丰富的平台、布局和基础组件。

基础库
基础库由Dart编写,提供了用Flutter构建应用所需的基本的类和函数,例如与引擎通讯的API。Flutter是通过组织、创建不同的组件完成用户界面设计的。在Flutter中,一个组件代表用户界面中不可变的一部分;包括文本、多边形以及动画在内的所有图形都是用组件创建的。复杂的组件由简单的组件结合而成。


Dart 3将在2023年成为100%健全的空安全语言

Dart 语言即将告别 null 值,开发者们必须在 Dart 3 正式到来之前调整自己的代码。2022年12月中旬消息,在 Dart 编程语言的第三个主要版本于 2023 年年中首度亮相时,将不允许在非必要位置使用 null 值。在该上下文中,null 代表一个赋值,用于表示不存在值或引用的对象。Null 引用最早可以追溯到 1964 年左右,当时英国计算机科学家 Tony Hoare 在 ALGOL 语言家族中引入了这个概念。他认为这是个“价值十亿美元的错误”,这里指的是修复此错误将要耗费的时间和金钱总值。这个判断相当精准,毕竟时至今日 null 仍然相当令人头痛。从 2.12 版本开始,Dart 逐渐获得了健全的 null 安全性支持——一种防止对被设定为 null 的变量进行错误访问的方式。虽然出手修复,但 Dart 仍然保留了不加 null 安全(或部分 null 安全)保护直接运行代码的模式。但从 Dart 3 开始,这些非最优解将彻底消失。

让 Dart 成为更安全的编程语言

不少现代编程语言都支持 null 安全(也称空安全),解决的是 null 引用的问题。Tony Hoare 曾将其称为一个价值十亿美元的错误:“这导致了无数的错误、漏洞和系统崩溃,在过去四十年间造成了价值十亿美元的痛苦和损失。”为了解决这个问题,Swift、C# 和 Kotlin 等语言开始支持一种类型系统,开发者可以在其中将变量声明为非 null(永远不能保存为 null 值)或可 null(可以保存为 null 值)。这个类型系统可以与静态分析配合使用,检测是否将 null 赋值给了不可为 null 的变量。Dart 语言中的 null 安全支持也采取类似的模式,默认情况下变量不可为 null,仅在显式声明时才允许为 null。在此基础上Dart更进一步,引入了健全 null 安全机制,这种健全机制保证,意味着不可为 null 的变量永远不会被赋予 null 值。

并不是每种语言的 null 安全实现都能如此稳定:Swift 在设计之初就考虑到了健全性;TypeScript 本质上并不健全,因为其底层类型系统允许将任何对象视为任意静态类型。所以在 TypeScript 当中,开发者其实仍可以将 null 值分配给非 null 变量。至于C#出于使用习惯和避免迁移所有当前代码的考虑,其中仍保留了几个例外。Kotlin 同样存在几个不合理的例外,部分原因是为了保持与 Java 的互操作性。举例来说,泛型类型可能导致声明包含非 null 元素的列表中流入 null 值。Dart 与 Flutter 产品经理 Michael Thomsen 发布博文解释称,“下个版本 Dart 3 将彻底完成通往绝对 null 安全语言的发展之旅。作为旅程的最后一步,将删除各个遗留 Dart 语言及 SDK 工件,不再支持非 null 安全条件下的运行模式。”

迁移到 Dart 3

但这一切也是有代价的。如果没有健全的 null 安全,由 2.12 或更早版本生成的带有 SDK 约束设置的 pubspec 文件(Dart 包的元数据)将根本无法兼容 Dart 3:“在 Dart 3 中,健全 null 安全将成为唯一受支持的模式。使用 2.12 及更早 SDK 约束版本的 pubspec 文件将无法在 Dart 3 或后续版本中正常解析。当约束设置为小于 2.12(例如 e.g. // @dart=2.9)时,任何包含语言标记的源代码都将报错。”

自 2.12 版本起,Dart 语言引入 null 安全已经有三年时间了,Dart 语言开发团队也意识到迁移现有 Dart 包和应用程序所造成的影响。为了降低迁移门槛,Dart 支持以三种方式运行应用程序代码,首先,可以在不启用 null 安全的前提下运行,也可以在启用部分 null 安全的混合模式下运行,最后可以在采取健全 null 安全的情况下运行。当代码 100%(包括所有依赖项)完成迁移时,即可实现健全 null 安全。如此一来,Dart 开发者就有时间一步步完成代码迁移。但请注意,支持多种模式也会增加维护开销和代码复杂度。

首先,Dart 开发者应当了解这三种模式选项。在阅读一段 Dart 代码时,首先应检查语言版本,据此判断类型会被默认为非 null、默认为可 null 或者二者兼有。其次,编译器和运行时若支持全部三种模式,则会拖慢 Dart SDK 的开发速度。这意味着新功能的添加成本和复杂度都会随之提升。关于迁移的更多信息,谷歌也提供了参考迁移指南

根据 Thomsen 的解释,约有 85% 的 Flutter 代码(用 Dart 编写而成)能够直接支持健全 null 安全。而对于余下的 15% 应用程序和软件包,开发者们必须在 Dart 3 正式到来之前调整自己的代码(please migrate before Dart 3 ships)。大型代码库的迁移可能需要一些时间。目前,德国汽车制造商宝马公司最近刚刚对其 MyBMW 应用进行了一轮 null 安全代码修订。这是一款由约 300 人开发者团队用 Flutter 构建的大规模应用程序。宝马公司移动应用开发主管 Christian Schmid 表示,“虽然对于像 MyBMW 应用这样的大型代码库来说,null 安全迁移工作并非易事,但谷歌提供的工具确实在迁移中给了我们很大帮助。在迁移完成之后,我们终于得到了一套更不容易出错的代码库。”

在 Dart 3 发布之后,该语言的下一个重要里程碑可能是支持将 Dart 代码编译为 Wasm,这样Flutter Web应用程序就能在浏览器中以本机代码的形式运行;当然这项工作需要 W3C 和浏览器开发商合作,通过 WasmGC 扩展让 Wasm 能够支持各类垃圾收集语言(包括 Dart)。


最新版本:2
Dart 2.12 已于2021年3月初发布,此版本带来了稳定的空类型安全声明 (sound null safety) 和 Dart FFI。空类型安全声明可帮助开发者避免 null 错误——这类错误往往很难被发现;FFI (Foreign Function Interface) 是一种互操作性机制,开发者可通过它调用使用 C 语言编写的现成代码,例如调用 Windows Win32 API。

空类型安全声明 (sound null safety)
这是自 Dart 2.0 引入声明类型系统 (sound type system) 以来,对 Dart 语言最大的补充。空类型安全 (null safety) 进一步增强了类型系统,让开发者在开发阶段即可捕获 null 错误,从而防止在生产环境出现崩溃,毕竟 null 错误是导致应用崩溃的常见原因。Dart 的空类型安全支持基于三条核心原则:
默认情况下不可为空、逐步采用空类型安全声明、完全空类型安全声明

Dart FFI,用于将 C 类库与 Dart 集成
Dart FFI 让开发者能够利用 C 类库中现成的代码,以实现更好的可移植性,并且通过与高度调整的 C 语言代码集成以执行对性能要求较高的任务。从 Dart 2.12 开始,Dart FFI 已经脱离 beta 阶段,并被认为是稳定功能,可用于生产环境。此外还新增了嵌套结构和按值传递结构。

嵌套结构
C API 通常使用嵌套结构——即本身包含结构体的结构,示例如下:
struct Wheel {
  int spokes;
};
struct Bike {
  struct Wheel front;
  struct Wheel rear;
  int buildYear;
};
从 Dart 2.12 开始,FFI 也已支持嵌套结构。

按值传递结构
开发者可以在 C 语言代码中按引用和按值传递结构。FFI 以前仅支持按引用传递,但从 Dart 2.12 开始,开发者可以按值传递结构。这是两个同时通过引用传递和值传递的 C 函数的示例:
struct Link {
  double value;
  Link* next;
};
void MoveByReference(Link* link) {
  link->value = link->value + 10.0;
}
Coord MoveByValue(Link link) {
  link.value = link.value + 10.0;
  return link;
}

发布公告还详细介绍了 FFI 的其他内容,详情点此查看。

最新版本:3
Dart 3 现已于2023年5月中旬发布,这是迄今为止最大的 Dart 版本,包含了三个主要改进:完成了 100% 健全的空安全;添加了记录、模式和类修饰符的新语言特性;以及对未来进行了预览,即,通过 Wasm 编译扩大了对网络原生代码的平台支持。


百分百健全的空安全
Dart 3 已经成为了一种 100% 健全的空安全语言。该特性为 Dart 带来了健全的类型系统。如果一个类型说一个值不是 null,那么它永远不可能是 null。这避免了某些类别的编码错误,例如空指针异常。它还允许编译器和运行时以没有空安全性无法实现的方式优化代码。虽然这种设计会使得迁移变得有点困难,但开发团队认为,他们为 Dart 做出了正确的选择。预计绝大多数已迁移到 null safety 的包和应用程序都可以与 Dart 3 一起使用。在少数情况下,Dart 3 中的少量相关清理可能会影响某些代码。一些旧的核心库 API 已被删除并且一些工具已被调整。如果用户在迁移到使用 Dart 3 SDK 时遇到任何问题,可查阅 Dart 3 迁移指南

主要语言特性 —— 记录、模式和类修饰符
Dart 3 不仅仅是基于现有基础作出改变,它还涉及添加重要的新特性和功能。

使用记录构建结构化数据
借助记录可以使用简洁明了的语法构建结构化数据。
( String , int ) userInfo( Map < String , dynamic > json) { return (json[ 'name' ] as  String , json[ 'height' ] as  int ); }

记录是值类型,没有标识。这使得编译器能够在某些情况下完全擦除记录对象。记录还带有自动定义的 == 运算符和 hashCode 函数。更多详细信息可查看文档。

用模式和模式匹配来处理结构化数据
记录简化了构建结构化数据的方式。这不会取代使用类来构建更正式的类型层次结构。它只是提供了另一种选择。在任何一种情况下,你都可能希望将该结构化数据分解为单独的元素以使用它们。这就是模式匹配发挥作用的地方。考虑模式的基本形式。以下记录模式将记录解构为两个新变量 name 和 height。然后可以像任何其他变量一样使用这些变量,例如调用 print 时:
var (String name, int height) = userInfo({'name': 'Michael', 'height': 180});
print('User $name is $height cm tall.');

类似的模式也存在于 lists 和 maps 中。对于所有这些,你可以使用下划线模式跳过单个元素:
var (String name, _) = userInfo(…);

Dart 3 中还扩展了 switch 语句的功能和表现力。现在已经消除了在每个案例末尾添加 break 的需要,还支持逻辑运算符来组合案例。

模式的一个强大功能是能够检查 “exhaustiveness”,此功能可确保 switch 可以处理所有可能的情况。
sealed class Animal { … }
class Cow extends Animal { … }
class Sheep extends Animal { … }
class Pig extends Animal { … }

String whatDoesItSay(Animal a) =>
switch (a) { Cow c => '$c says moo', Sheep s => '$s says baa' };

这将返回以下错误,提醒错过了最后一个可能的子类型 Pig 的处理:
line 6 • The type 'Animal' is not exhaustively matched by the switch cases
since it doesn't match 'Pig()'.

if 语句也可以使用模式:
final json = {'name': 'Michael', 'height': 180};

// Find Michael's height.
if (json case {'name': 'Michael', 'height': int h}) {
  print('Michael is $h cm tall.');
} else {
  print('Error: json contains no height info for Michael!');
}

更多详情可查看 patterns documentation 和 patterns codelab。

具有类修饰符的类的细粒度访问控制
Dart 3 的第三个语言特性是类修饰符。与期望每个 Dart 开发人员都使用的记录和模式不同,这更像是一个高级用户功能。它满足了 Dart 开发人员制作大型 API 表面或构建企业级应用程序的需求。类修饰符使 API 作者能够仅支持一组特定的功能。默认值保持不变。开发团队希望 Dart 保持简单易用。因此像以前一样,可以构造、扩展和实现常规类,如以下示例所示:
class Vehicle {
  String make; String model;
  void moveForward(int meters) { … }
}

// Construct.
var myCar = Vehicle(make: 'Ford', model: 'T',);

// Extend.
class Car extends Vehicle {
  int passengers;
}

// Implement.
class MockVehicle implements Vehicle {
  @override void moveForward …
}

类修饰符支持在此基础上添加限制,考虑一些示例用例:
使用 interface class,你可以定义一个契约供其他人实施。不能扩展接口类。
使用 base class,你可以确保类的所有子类型都继承自它,而不是实现它的接口。这确保私有方法在所有实例上都可用。
使用 final class,你可以关闭类型层次结构以防止你自己的库之外的任何子类。作为一个 sample 好处,这允许 API 所有者添加新成员,而不会冒破坏 API 使用者更改的风险。

展望未来
查看 Dart 3 之后的次要版本是否需要对记录、模式和类修饰符进行更新。研究一些更小、更增量的功能,这些功能完全不具破坏性,并且专注于提高开发者的生产力,而没有迁移成本。正在探索的两个示例是用于使用零成本 “wrappers” 以包装现有类型的内联类,和 primary constructors,它引入了一种更简洁的语法来定义具有几个字段和一个 primary constructor 的类。关注宏(也称为元编程),以便更好地反序列化 JSON(和类似的),并启用数据类。目前已经支持用 dart:fi 编译成 C 库的代码的互操作。正在努力将其扩展到支持 Android 上的 Java 和 Kotlin 互操作,以及 iOS/MacOS 上的 Objective C 和 Swift 互操作。编译为 WebAssembly —— 使用 native code 定位 web。

更多详情可查看官方发行公告

Dart 3.2 现已于2023年12月中旬发布。该版本针对以下方面做出了改进:新增了一项语言功能,可对私有 final 字段进行非空升级;通过新增的互操作功能改善了开发者体验;支持 DevTools 的扩展程序;并更新了 Web 路线图,包括提供对 Wasm (又名 WebAssembly) 的支持。

私有 final 字段的非空升级
自 Dart 2.12 中引入健全的空安全 (sound null safety) 以来已过去几年时间。Dart 3.2 中改进了流程分析引擎,以便够对私有 final 字段实施类型升级。可以这样理解:对于私有 final 字段,它的值在初始分配后永不更改,因此仅检查一次也是安全的。私有 final 字段升级从 Dart 3.2 起推出,并将应用于配置 3.2 及以上版本 Dart SDK 的项目。

在 package:lints 3.0 中加入新的代码分析选项
针对代码分析,还对 package:lints 中的标准代码分析规则进行了一些改进。package 包含默认和推荐的静态分析规则集,适用于任何根据 dart create 或 flutter create (通过 package:flutter_lints — package:lints 的扩展) 创建的新项目。lint 集新的主要版本 (版本 3.0) 现已推出。项目团队在此修订版的核心集中添加了六个 lint,在推荐集中添加了两个 lint,可用于验证 pubspec URL,以及验证是否使用正确的参数调用集合方法等。

Dart 互操作性更新
从 Dart 3.2 开始,对原生互操作进行了许多改进:
1.为 C FFI 引入了 NativeCallable.isolateLocal 构造函数,它可以根据任意 Dart 函数创建 C 函数指针。这是一项由 Pointer.fromFunction 提供的扩展功能,只能根据顶级函数创建函数指针。

2.更新了 Objective-C 绑定生成器,以使用在 Dart 3.1 中添加的 NativeCallable.listener。该生成器现在可以自动处理包含异步回调的 API,例如 Core Motion 这类此前需要手动编写部分绑定代码的 API。

3.为实现 Java 和 Kotlin 互操作而持续改进 package:jnigen。现在能够将 package:cronet_http (适用于 Android 的 Cronet HTTP 客户端的封装容器) 从手写的绑定代码迁移到自动生成的封装容器。

4.在 Native Assets 功能方面取得了重大进展,该功能旨在解决与依赖原生代码的 Dart package 分发相关的许多问题。Native Assets 提供统一的钩子来集成构建 Flutter 和独立 Dart 应用所涉及的各种构建系统,从而解决相关问题。您可以查看相关文档获取预览。

适用于 Dart package 的 DevTools 扩展程序
Dart DevTools 是一套用于支持纯 Dart 和 Flutter 应用的调试及性能工具。项目团队在 Dart 3.2 和 Flutter 3.16 中推出了新的扩展框架,让 package 作者能够直接在 DevTools 中为其 package 构建自定义工具。因此包含框架的 pub.dev package 能够提供特定于其用例的自定义工具。“例如,Serverpod 的作者一直在努力为其 package 构建 DevTools,并且很高兴在即将发布的 1.2 版本中提供 DevTools 扩展程序。”

Dart Web 和 Wasm 更新
项目团队正在将高级托管语言 (如 Dart) 的垃圾回收功能添加到 Wasm 标准中。Dart-to-Wasm 编译器几乎配备所有功能。“我们对性能和兼容性非常满意,正在进一步关注边缘情况,以确保在各种场景中实现畅快运行。”

对于 Flutter Web 则完成了一个新的 "Skwasm" 渲染引擎开发。为了最大限度提高性能,Skwasm 通过 wasm-to-wasm 绑定,将编译后的应用代码直接连接到自定义 CanvasKit Wasm 模块。这是 Flutter Web 多线程渲染支持的首次迭代,进一步提高了帧时间。在采用 Wasm 的 Flutter Web 准备结束目前的实验状态之前还计划:
1.双重编译:生成 Wasm 和 JavaScript 输出,并在运行时启用功能检测,以支持具备或不具备 Wasm-GC 支持的浏览器。

2.现代 JavaScript 互操作性:一种新的基于扩展类型的 JS 互操作机制,当针对 JavaScript 和 Wasm 时,可以在 Dart 代码、浏览器 API 和 JS 库之间实现简洁的、类型安全的调用。

3.支持 Wasm 的浏览器 API:新的 package:web,基于现代 JS 互操作机制,取代了 dart:html (及相关库),借此可轻松访问浏览器 API,并且支持在 JS 和 Wasm 目标上使用。

目前,其正在开始将大量的内部项目迁移到 package:web 和新的 JS 互操作机制,并希望在下一个稳定版本中向用户提供更多更新。


官方主页:https://www.dartlang.org/
该文章最后由 Administrator 于 2023-12-16 10:32:28 更新,目前是第 2 版。