MoonBit v0.10.9 版本更新
2026/8/26 18:44:07 网站建设 项目流程

MoonBit v0.10.4版本更新

对应moonc版本:v0.10.9

语言更新

  • with-pattern 现在需要在使用了 with 的分支显示加上括号,便于读者更容易理解with-pattern 的优先级。可以使用moon fmt进行自动迁移:

    fnmain{leta=Some("hello")matcha{Some(x)|(Nonewith x="")=>println(x)// ^~~~~~~~~~~~~~~~~~ 需要加上括号}}
  • bitstring pattern 支持 v128le,可以从字节序列中一次提取 16 个字节,构造成一个V128值。目前只支持以字节为单位的小端序,即输入的第 0 个字节对应结果的最低位字节:

    fnmain{letbits=Bytes::makei(16,i=>i.to_byte())guard!bits is[v128le(bits),..]println(bits)// 输出 V128(0x0706050403020100, 0x0f0e0d0c0b0a0908)}
  • lexscan正式进入 stable 状态。

    • lexscan支持@lexbuf.Lexbuf@lexbuf.AsyncLexbuf以及@lexbuf.StringScanner。其中LexbufAsyncLexbuf为 streaming 模式,我们对内存占用做了优化,可以放心在 infinite stream 上使用

    • 此前的lexscanonString/StringView迁移到使用lexmatch关键字

    • 现在 catch-all case 在明确不可达的情况下不再需要添加,并且编译器会对不可达的 cases 报告警告

    ///|asyncfnwordcount(input:@lexbuf.AsyncLexbuf,lines:Int,words:Int,chars:Int,)->(Int,Int,Int){lexscan input{re"^\n"=>wordcount(input,lines+1,words,chars+1)re"^[^ \t\r\n]+"asword=>wordcount(input,lines,words+1,chars+word.length())re"^."=>wordcount(input,lines,words,chars+1)re"^"=>(lines,words,chars)}}///|asyncfnmain{letutf8_reader=Utf8Reader(()=>@stdio.stdin.read_some())letlexbuf=@lexbuf.AsyncLexbuf::from_fn(()=>utf8_reader.read())let(lines,words,chars)=wordcount(lexbuf,0,0,0)println("lines: \{lines}, words: \{words}, chars: \{chars}")}

    具体请参考文档 https://docs.moonbitlang.com/en/latest/language/fundamentals.html#lexmatch,
    https://docs.moonbitlang.com/en/latest/language/fundamentals.html#lexscan

  • 引入了新的errdefer语法:

    errdefer expr rest

    如果rest抛出了错误或者作为一段异步代码被取消了,errdefer就会被触发,执行expr,然后把错误继续向上抛出。errdefer对于构造器类的函数尤其实用,例如:

    asyncfnconnect_to(addr:@socket.Addr)->Tcp{letsocket=make_tcp_socket()errdefer socket.close()connect_socket(socket)socket}

    connect_to成功返回时,socket的所有权会随返回值转移至调用方,因此当前作用域无需释放该资源。若connect_socket(socket)执行失败或被取消,socket会被丢弃,此时若不释放socket,就会导致资源泄漏。errdefer适用于此类仅在错误路径上执行清理的场景,可用于确保资源得到可靠释放。

    使用return/break/continue跳出errdefer的范围不会触发errdefer。和defer一样,errdefer是结构化的:它只会在程序离开整个errdefer表达式的范围时触发。所以,下面的程序是错误的:

    letresult=[]forxinxs{letres=make_resource(x)errdefer res.close()do_something_with_res(res)result.push(res)}result

    这里,每个errdefer仅在其所在的单次循环作用域内有效。第一次循环正常结束后,对应的errdefer即不再生效。当后续循环发生错误时,只会执行当前循环对应的清理逻辑,此前循环中已成功创建的资源无法得到释放,进而导致资源泄漏。正确的写法是:

    letresult=[]errdefer result.each(res=>res.close())forxinxs{letres=make_resource(x)do_something_with_res(res)result.push(res)}result
  • defererrdefer支持raiseasync。之前,defer exprexpr中不能抛出错误或是调用异步代码。这一限制现在已被解除。如果defer/errdefer中抛出错误,新的错误会替代旧的错误。有多条defer/errdefer语句时,如果其中某处defer/errdefer抛出了错误,剩下的defer/errdefer依然会按顺序执行,不会被丢弃。

  • 新增将catch迁移至defer/errdefer的警告。

    目前,在moonbitlang/async中,被取消的异步程序会抛出一个特殊的错误作为被取消的信号,以方便被取消的程序释放资源。但这个特殊错误有可能被意外捕获、转换,从而导致程序在被取消时出现错误。未来,我们计划不再使用特殊错误来表示取消信号,并让catch不再捕获取消信号。但如果程序依赖于catch来执行资源清理,这一改动会导致程序在被取消时无法正确释放资源。因此,我们引入了errdefer并解除了defer的副作用限制,以保证几乎所有资源释放代码都可以用defer/errdefer表达(取消信号未来也依然会触发defererrdefer)。

    为了帮助用户迁移现有的、基于catch的资源释放代码,我们提供了一个新的警告fragile_catch_all。它会识别可能可以改写成defer/errdefercatch表达式,并给出警告提示用户迁移。除了在未来能正确处理异步取消之外,defer/errdefer本身相比catch也更加可读和健壮。

    这一新警告可能会出现误报的情况。如果发生了误报,可以通过#warnings("-fragile_catch_all")对当前函数临时关闭警告。

  • guard现在会进行完备性检查,对于不完备的 pattern 会报警告。对于希望使用之前的guard无法匹配则 panic 的语义的用户应该迁移到guard!来更加明确地表达自己的意图。

    fnmain{letstring=Some("content")guard string isSome(content)// ^~~~~~ Warning (guard_inexhaustive):// This `guard` pattern is not exhaustive and will panic when// it does not match. Missing cases:// None// To fix: add an `else { ... }` clause after the condition to// handle those cases, or write `guard!` if the panic is intended.guard!string isSome(content)// 推荐的新写法println(content)}
  • 支持 labelled block,给代码块加上标签后,块内可以用break携带一个值提前退出,该值就是整个块的求值结果。

    fnabsolute(n:Int)->Int{result~:{ifn<0{breakresult~(-n)}n}}

    Labelled block 不存在匿名形式:不带 label 的break始终以最近的循环为目标,而不会作用于某个 block。为了避免阅读代码时的歧义:直接出现在 labelled block 内的不带 label 的break会被视为错误,即使外层存在可作为跳转目标的循环。此时必须显式指定 label,以明确需要退出的控制流层级。

    fnf()->Int{for;;{label~:{break1// ^^^^^^^ An unlabelled `break` is not allowed directly inside a labelled block.}}}
  • 增加了新的保留字nocancel

  • #warnings现可作用于语法警告。此前,它仅支持屏蔽类型检查阶段的警告,deprecated_syntax等语法警告不受影响。现在,#warnings已可局部屏蔽大多数警告,包括语法警告;部分跨顶层定义的警告和词法警告仍不支持。

工具链更新

  • 默认后端改为 wasm

  • 现在moon prove只需要用户安装至少一个支持的求解器 (Z3 / Alt-Ergo / CVC5) 即可直接使用,不再需要单独安装 Why3。相应的, 现在 Why3 的data-dirlib-dir不支持通过环境变量指定,始终读取~/.moon/share/why3/~/.moon/lib/why3/

  • 现在可以通过moonx username/example[@version]的方式来执行 mooncakes.io 上面的 WASM executable (默认以 WASM 后端执行,可以通过--target native的方式以 native 后端执行):

    $ moonx moonbit-community/moongrep error: the following required argument was not provided:'subcommand'Usage: moongrep<command>Scan MoonBitsourcefiles with structural and taint rules. Commands: scan Scan MoonBitsourcefiles. lint Scan MoonBitsourcefiles with embeddedbuiltinrules. docs Print embedded moongrep documentation. dump Parse a MoonBit impl or expression and print untyped_ast debug output.helpPrinthelpforthe subcommand(s). Options: -h,--helpShowhelpinformation.
  • 支持.moonignore文件

    • 之前如果用户需要显示指定某些文件是否应该包含在 moon publish 发布到 mooncakes.io 上面的 module 的话,需要通过.gitignore配合 moon.mod 中的excludeinclude字段来配置,修改起来不方便。

    • 现在打包时遵守通行的 ignore 文件规则。选择文件夹中的.moonignore或(如前者缺失).gitignore作为 ignore 文件。

    • 默认忽视.开头文件和文件夹(可通过 ignore 文件覆写规则),_build文件夹(不可覆写规则)。

    • excludeinclude字段将被废弃。

  • mooncakes.io 现已禁止上传仅大小写不同的包。此前,如user/pkgauser/pkgA这样的包可以同时存在,但可能在大小写不敏感的平台上产生冲突,因此现在会对这类包名进行限制。

标准库更新

  • moonbitlang/core

    • QuickCheck 更新

      • 支持@qc.check(失败会 raise 一个错误,成功则不打印其他东西) 和@qc.report(返回结构化的测试报告)两种主要测试函数,用户可以传递函数(A) -> Bool raise?来进行基于属性的测试

      • 可以向测试函数传递filter?: (A) -> Bool参数来过滤 Generator 一些不满足要求的值,利用参数discard_ratio可以控制测试在丢弃多少比例的时候会失效

      • @qc.Generator[T]类型和相关的函数提供了一套常用的组合子,可用于辅助构建Arbitrary实例

      • core/quickcheck/shrink包提供了大部分常用类型的 Shrinker (收缩器),可在找到反例的时候进行收缩,以寻找更小更简单的反例

      • 支持统计分析功能,可以通过observe?参数给 check / report 传递一个观测组合(A) -> Observation,其中Observation可以使用如下函数构造:

        • @qc.label(val: String)标注一个字符串标签

        • @qc.classify(cond: Bool, val: String)在条件cond成立的时候打上标签val

        • @qc.collect(val : T)把一个值的 Debug Repr 作为标签

      • 更多具体细节可以参考文档:https://mooncakes.io/docs/moonbitlang/core/quickcheck

    • 新增moonbitlang/core/diff

      • 提供通用的两种序列Diff算法Myers和Patience, 用户可以通过@diff.Diff(old~, new~).edits()获取两个序列的编辑脚本,

      • 提供多个计算不同序列编辑距离的函数edit_distance(ArrayView[T]),edit_distance_str(StringView),以及对应的限制最大编辑距离的版本

    • 新增moonbitlang/core/lexbuf包,提供StringScannerLexbufAsyncLexbuf, 以配合 lexscan 进行使用。

      • StringScanner:同步的 String based scanner,由 lexscan 维护 scanner 上的 cursor 字段

      • Lexbuf/AsyncLexbuf:Streaming scannar,由Lexbuf::from_fn定义数据源,lexscan 过程中会自动 refill。两者区别在于 lexscanAsyncLexbuf的表达式整体是要求 async 上下文的。

    • immut/array包之前已经 deprecate 了很长一段时间,现在正式删除,应该改用immut/vector

    • @debug.to_repr(x)弃用,可改用@debug.Repr(x)

  • moonbitlang/async目前最新版本为 0.21.0,自上次月报(0.20.2)以来的主要更新有:

    • [breaking]@http包中,HTTP headers 的类型从Map[String, String]变成了大小写不敏感的type @http.Headers = Map[@http.CaseInsensitiveString, String],因此构造和读取 HTTP header 时不再需要手动注意大小写问题。@http.CaseInsensitiveString可以从String隐式构造,因此直接使用Map字面量构造 header 和读取 header 的代码无需修改。但对 header 写了类型标注的代码需要将类型改为@http.Headers

    • [breaking]@fs.open等 API 的createtruncate参数已被废弃一段时间,由create_modepermission代替。这次更新中,createtruncate被正式移除。此外,@fs.write_file@process.redirect_to_file的默认create_modeOpenExisting变成了CreateOrTruncate@fs.open的默认create_mode则依然是OpenExisting

    • [breaking]@async.protect_from_cancel的默认行为变为resume_on_cancel=trueresume_on_cancel=false选项被废弃。未来只会有resume_on_cancel=true的行为

      protect_from_cancel(resume_on_cancel=false)在被取消时,会保证内部的代码完整运行,然后丢弃其结果并抛出取消信号。这里被丢弃的结果可能导致资源泄露,因此是不安全的

      对大部分用户的代码来说,这一行为变动不会产生实质性的影响

    • moonbitlang/async现在会在程序陷入死锁状态(例如两个任务互相等待)时,自动检测到死锁并强行终止程序,防止事件循环无限空转。可以通过@async.set_deadlock_handler控制死锁时的行为或是关闭死锁检测

    • 之前,moonbitlang/async必须在主线程中运行自己的事件循环,因此无法与其他外部事件循环,例如 GUI 框架自带的事件循环整合。本次更新新增了@async.set_external_event_loopAPI,可以用于设置一个外部事件循环。moonbitlang/async会将自己的事件循环运行在单独的线程里,并和主线程的外部循环整合。所有 MoonBit 代码依然会运行在主线程里。关于外部事件循环的实现需要为moonbitlang/async提供哪些 API,详见@async.set_external_event_loop的文档

    • 新增了@process.pipeAPI,可以用于将一个子进程的输出重定向到另一个子进程的输入

    • @process.read_from_process@process.redirect_to_file新增了shared? : Bool = false参数。如果shared=true,用于重定向的输出管道可以被同时传给多个子进程,但必须在最后一个子进程启动后通过.close()手动关闭。如果shared=false(默认行为,和之前相同),用于重定向的输出管道只能被传给一个子进程(但可以同时传给同一个子进程的stdoutstderr),不过无需手动关闭

    • 新增辅助函数@http.request,可以用于执行任意方法的单次 HTTP 请求

    • Wasm1 后端新增了@websocket@fs.realpath支持,现已支持除@fs.Watcher外的所有功能

    • 在 Linux/MacOS 上,当子进程被某个信号终止,而非正常退出时,@process.run等 API 能识别这种情况并返回-signal

    • 当一个async fn main程序被信号取消时,之前async fn main会在程序释放完资源后以128 + signal作为返回值退出(bash convention)。但这一行为对父进程来说是有歧义的。现在,async fn main在被信号取消时,会在程序完成清理后,重新模拟出当前进程被信号强制中止的状态作为程序的退出状态

  • moonbitlang/x

    • 弃用了moonbitlang/sys,应该改用moonbitlang/core/env

    • moonbitlang/x/path现在可以在浏览器环境正常使用,Windows 的路径比较现在可以正确处理非 ascii 且有大小写形式的字符。

    • moonbitlang/x/rational现在不会出现溢出误判和分母零问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询