一位Haskell开发者偶然翻出那本被称作“巫师书”的《计算机程序的构造和解释》(SICP),翻到第3.3节时,一个想法冒了出来:书中用可变状态和消息传递搭建的数字电路模拟器,能不能用纯函数式的Haskell复现出来?
答案让人意外——不仅行得通,而且实现出来的结构和原版几乎一致。关键在于IORef这套用来管理可变引用的机制。虽然Haskell以纯粹性著称,但它同样提供了可控的副作用入口,IORef就是其中最直接的一种:一个可以在IO上下文里读写的可变容器。
打开网易新闻 查看精彩图片
整个模拟器围绕一条“导线”展开。在SICP的原始方案里,导线既承载信号值,也挂着一组“动作过程”。每当导线上信号发生变化,这些动作过程就会被依次触发——下游连着这条导线的器件,藉此获知变化并更新自身,这正是观察者模式的经典用法。Haskell版本用一个记录类型Wire,把这两个信息分别装进两个IORef:一个存信号值,另一个存动作过程的列表。
当需要设置信号值时,函数会先读出当前信号,只有在新值与旧值不同的前提下,才写入新值并依次执行所有已安装的动作过程。这样一来,就避免了因信号不变而引发的不必要连锁更新。获取信号只是简单地读取对应的IORef,而安装新的动作过程则是向列表添加订阅者。为了让器件在初始连接时获得正确的信号状态,仿真框架还会在安装动作过程时立即用当前信号值触发一次回调。
SICP原版动画里,那些隐藏的可变状态、全局调度器,乃至面向对象的自循环结构,在Haskell里全都被IORef和延迟执行的IO动作组合平稳接住了。原本只活在动态语言里的电路仿真风格,在强类型、惰性赋值的语言环境下,依然能靠几个基本的可变原语,把信号传播、状态变更和依赖链全部复刻出来。
热门跟贴