用Scheme写网页游戏,这件事到底靠不靠谱?Hoot给出了一个阶段性回答:它是一个把Scheme编译到WebAssembly的编译器后端,服务于Guile,同时也是一套通用WebAssembly工具链。用发布公告里的话说,就是“浏览器里的Scheme”。0.10.0版本已经发布,距离5月的0.9.0过去了一段时间,这次带来的是新功能和一批bug修复。

发布者Dave Thompson在公告里直接抛出了一个场景:即将到来的Lisp Game Jam。这场秋季赛将于2026年10月30日星期五开始,为期10天,参与者用自己偏好的Lisp方言做游戏。如果“用Hoot在Scheme里做一个小型网页游戏”听起来有意思,官方准备了一个起步模板。

这个模板项目把做HTML5游戏需要的东西打包好了:

  • 与必要Web API的绑定,用来配合HTML5 canvas做出可交互的游戏;
  • 一个Makefile,负责编译、启动开发用Web服务器,以及生成可上传到itch.io的.zip包;
  • 一个非常简单的打砖块类示例游戏,演示各部分如何拼在一起。

公告还列出了往届Game Jam中用Hoot做出的游戏作为参考。接下来是发布说明本身,改动分成编译器、运行时、Scheme层、命令行、文档和浏览器兼容几块。

编译器与运行时

编译器方面,重复导入报错现在会带上模块名,便于定位;为兼容R6RS加入了库版本支持;修复了define-module库的默认版本问题;还修了locate-library不遵守已配置加载扩展的问题——此前它用的是Guile的加载扩展参数,而不是Hoot自己的。

运行时方面,新增了%struct-ref和%struct-set!两个原语,用于访问解释器定义的记录类型。运行时模块系统在导入未定义符号时不再报错,原因是Guile对此比较宽松,Hoot过于严格反而造成兼容性问题。此外修复了primitive-eval的可选参数解析,以及同时带可选参数和关键字参数的过程的关键字参数解析;运行时模块注册表相关错误信息现在会给出正确的命令行标志:-fruntime-modules。

Scheme层的大面积补齐

这一块的改动最密集。SRFI-13字符串库和SRFI-60整数按位操作都只做了部分实现。SRFI-13之所以只是一组再导出,原因很具体:Guile的SRFI-13模块不是声明式的,会被Hoot编译器拒绝。

向(guile)模块加入了define-macro、defmacro、delete、list->symbol和symbol,导出了define-syntax-parameter、syntax-parameterize、symbol-append、integer-length和gensym;在(hoot bitvectors)中加入list->bitvector和bitvector并再导出到(guile);在(rnrs io ports)中实现了get-bytevector-all;bytevector=?成为(hoot bytevectors)和(rnrs bytevectors)公开导出的过程。

更显眼的是R6RS模块的成批落地:(rnrs)、(rnrs base)、(rnrs syntax-case)、(rnrs r5rs)、(rnrs hashtables)、(rnrs files)、(rnrs enums)、(rnrs exceptions)、(rnrs arithmetic fixnums)、(rnrs records inspection)、(rnrs records procedural)、(rnrs records syntactic)、(rnrs io simple)、(rnrs io ports)均已加入。(rnrs bytevectors)被重写为版本(6)的R6RS模块。

修复清单里也有几处值得一提:number->string现在会把浮点小数部分舍入到偶数位,并修好了±2^N浮点数在特定转换算法下的问题;min/max宏在零参数时不再失效;(guile)中log过程的参数得到修正;negate的实现被修好——公告的原话是它“完全做错了事”。

命令行与兼容性

命令行层面,端口号现在会校验为[1, 65535]范围内的精确整数;无效选项或未知子命令终于会真的报错,此前负责这件事的代码根本不可达。运行hoot compile --run=node时会加上--experimental-wasm-exnref特性标志,以兼容那些默认未启用新Wasm exnref特性的NodeJS版本。还修复了构建期禁用hoot server的问题:过去hoot命令行脚本仍会尝试加载./configure已判定缺失的可选库,这个疏漏由mcc发现。

文档方面,手册“从源码构建”一节的说明得到改进,README的“进阶路径”部分提到了依赖,项目模板的README则更新为Guix并非必需,尽管它仍是最省事的选择。

浏览器兼容范围写得很明确:Safari 26或更高、Firefox 121或更高、Chrome 119或更高。获取方式上,Hoot可通过GNU Guix安装(guix pull后执行guix install guile guile-hoot),也已进入Debian,不过这个版本抵达Debian还需要一段时间;此外可以从发布压缩包从源码构建。公告最后致谢了Z572、stdh、Walter Lewis和Jelle Licht对本版本的贡献。