PROGRAMMING LANGUAGE THEORY · PLT
编程语言理论可视化实验室
从 Lambda 演算到内存模型,八个交互式可视化模块带你深入编程语言的核心理论。每个模块都包含可交互的 SVG 图形、实时控制面板与统计数据,纯 HTML/CSS/JS + SVG 实现,零外部依赖。
8 Modules
SVG + Vanilla JS
Zero Dependencies
Single File
01LAMBDA CALCULUS
Lambda 演算
Lambda 演算是函数式编程的数学基础。这里展示 β-归约(β-reduction)与 α-转换(α-conversion):将抽象应用于参数时进行变量替换,必要时重命名绑定变量以避免变量捕获。
归约过程可视化
β-归约规则:(λx.M) N → M[x:=N]。左下方选择预设表达式或自行输入,点击「归约一步」执行最外层归约,「完整归约」直到不可再归约。
02TYPE SYSTEMS
类型系统
Hindley-Milner 类型推断算法通过合一(unification)在 AST 上传播类型约束,自动推断表达式类型。切换 let-多态可观察 `let id=λx.x in id id` 在多态下推断为 a→a,在单态下发生 occurs check 失败。
类型推断与约束传播
HM 算法原理:为每个节点分配类型变量,通过 unification 求解约束。let-多态会对 let 绑定的值进行泛化,使其可在不同类型处被实例化复用。
03GARBAGE COLLECTION
垃圾回收
三种经典 GC 算法对比:Mark-Sweep 标记可达后清除、Copying 复制存活对象到半区并压缩、Generational 按代龄分代回收。分配对象、建立引用、触发回收,观察堆空间前后变化。
堆内存与回收过程
可达对象
垃圾(将被回收)
根集 Root
已释放
GC策略:从根集(栈/全局变量)出发追踪引用图,未被标记的对象即为垃圾。分代假设:多数对象朝生夕灭,年轻代回收更频繁。
04INTERPRETER
解释器
树遍历解释器(tree-walking interpreter)递归求值 AST。逐步执行可观察 eval 调用顺序与环境栈的演变:let 绑定扩展环境,变量查找沿环境链读取,if 根据条件选择分支。
执行轨迹与环境
eval求值规则:字面量返回自身;变量查环境;let 先求值绑定再扩展环境求值体;if 求值条件后选择分支;二元运算先求值两子节点再计算。
05CLOSURES
闭包
闭包 = 函数代码 + 被捕获的环境。当内部函数引用外部作用域变量时,即使外层函数已返回,捕获的环境依然存活。此处可视化环境链与捕获变量的生命周期。
环境链与捕获变量
闭包机制:makeAdder(10) 返回的闭包捕获了 x=10。即使 makeAdder 调用结束,闭包仍持有该环境,调用 adder(5) 时沿环境链找到 x,计算 10+5=15。
06MONADS
Monads
单子用 bind (>>=) 串联计算,封装上下文副作用。Maybe 处理失败短路、List 表达非确定性多重结果、State 传递可变状态。观察值如何流经 bind 链并发生类型变换。
Bind 链与类型变换
Just / 成功值
Nothing / 失败
列表元素
状态值
>>= bind:m >>= f 将 f 应用于 m 解包后的值。Maybe 中 Nothing 短路;List 中 f 返回列表需 concat 扁平化;State 中传递 (值, 状态) 对并更新状态。
07MACRO SYSTEMS
宏系统
卫生宏(hygienic macro)在展开时自动重命名绑定变量,避免变量捕获。展开前是宏调用语法树,展开后宏节点被替换为模板填充结果。对比展开树前后的结构差异。
宏展开树
卫生展开:swap(a,b) 宏引入临时变量 tmp,卫生宏确保该 tmp 不会与调用点既有的 tmp 冲突,通过 gensym 重命名为 tmp#1 实现隔离。
08MEMORY MODELS
内存模型
栈与堆的布局,以及值语义与引用语义的差异。切换语言观察:C 的手动栈/堆、Rust 的所有权移动、Java 的引用对象、Python 的一切皆对象引用。分配变量看其在内存中的位置。
栈 / 堆内存布局
语义对比:C 中 struct 默认栈上值拷贝;Rust let b=a 移动所有权使 a 失效;Java 对象在堆、变量存引用;Python 变量皆是引用绑定到堆对象。