MoonBit 新特性:Virtual Package 虚拟包机制
Moonbit 最近新增了一项特性:virtual package。通过将一个 package 声明为虚拟包,定义好一套接口(通过 .mbti 文件声明),**用户可选择具体使用哪一份实现,如不指定则使用该虚拟包的默认实现。通过这项特性,给分离接口与实现带来较大灵活性。**注意:目前这项特性处于实验性状态。
使用方法
以: virtual_pkg_demo 这个项目为例,项目结构如下:
.
├── virtual // 声明为 virtual package,其中的 virtual.mbti 定义了一套接口
├── impl // 为 virtual 包中的接口提供了一份自定义实现
├── lib // 依赖了 virtual 包中的接口
└── main // 依赖了 lib 包,声明了用 impl 包去 override 掉 virtual 包的默认实现
-
声明某个包为 virtual package:在 virtual 包的
moon.pkg.json
中用一下字段声明该包是一个virtual
包。包中需有 .mbti 文件否则报错,若"has-default"
为true
则会构建默认实现的编译产物,在没有 override 该包的情况下一起被链接"virtual": { "has-default": true }
-
为某个 virtual package 提供自定义实现:在 impl 包中的
moon.pkg.json
设置"implement"
字段为某个 virtual 包的完整包名。需要完整地实现 virtual package 中的.mbti
文件中规定的接口"implement": "username/hello/virtual"
-
使用 virtual package:在 lib 包的
moon.pkg.json
中的"import"
字段中引入 virtual 包 -
覆盖 virtual package:在
main
包的moon.pkg.json
中的"overrides"
字段中设置实现包。如果没有设置实现包且 virtual 包有默认实现则使用默认实现,无默认实现则报错{ "is-main": true, "import": [ "username/hello/lib" ], "overrides": [ "username/hello/impl" ] }
具体示例
下面通过一个小例子说明 virtual package 在 moonbitlang/core
中的使用。moonbitlang/core/abort
被定义为一个 virtual package。具体代码如下:
-
abort.mbti:声明了这个包中暴露出来的 api
package "moonbitlang/core/abort" // Values fn abort[T](String) -> T // Types and methods // Type aliases // Traits
-
abort.mbt:提供了
fn abort[T](String) -> T
这个 api 的默认实现。pub fn abort[T](msg : String) -> T { let _ = msg panic_impl() } ///| fn panic_impl[T]() -> T = "%panic"
在目前的默认实现中会故意忽略掉 msg
参数,这是因为如果使用 println(msg)
的话会导致生成的 .wasm 文件依赖 moonrun
运行时中定义的 spectest::print_char
函数,此 时若是使用除了 moonrun
之外的 wasm 运行时(如 wasmtime、wasmer 等)则会导致报错:
error: Unable to instantiate the WebAssembly module
╰─▶ 1: Error while importing "spectest"."print_char": unknown import. Expected Function(FunctionType { params: [I32], results: [] })
自定义 moonbitlang/core/abort
的实现
abort(msg)
接口在许多 builtin 数据结构中都有用到,在不满足某些 invariant 的情况下会在运行时失败退出,而按照目前的默认实现,abort 中的 msg 不会输出,用户也较难发现出错的原因。为此,我们可以自定义 abort 的实现,让他打印出 msg(当然,如果你不用 moonrun 以外的运行时运行编译好的 .wasm 的话),做法如下:
首先执行 moon add moonbitlang/dummy_abort
,然后在 moon.pkg.json
中增加以下字段:
"overrides": [
// 这个包给 moonbitlang/core/abort 这个 virtual package 提供了一份自定义实现
"moonbitlang/dummy_abort/abort_show_msg"
]
测试此时 abort 的行为,可以发现传入的参数确实被打印出来了
故意在一个数组上错误地使用 swap
,abort 报错信息如下
未来,moonbit 社区将会提供符合 wasi 标准的库去实现 println
,届时编译出来的 .wasm 也可以在支持此标准的 wasm 运行时上运行,敬请期待