name: effect-type-system
description: ‘实现代数效应类型系统。使用场景:(1) 处理副作用,(2) 可扩展效应,(3) 效应推断。’
version: 1.0.0
tags:
- 类型系统
- 效应
- 副作用
- 单子
difficulty: intermediate
languages:
- python
- haskell
dependencies:
- type-checker-generator
效应类型系统
实现代数效应类型系统,用于跟踪计算。
何时使用
此技能的功能
- 定义效应 - 代数效应签名
- 跟踪操作 - 效应计算
- 处理效应 - 效应处理器
- 推断效应 - 效应推断
核心理论
Effect Signature:
effect State: get : Unit → Int
put : Int → Unit
Effect Type:
带有效应集的计算
T => E (T 带有效应 E)
Handler:
handle expr with
return x => x
| get(k) => ...
实现
from dataclasses import dataclass, field
from typing import Dict, List, Set, Optional
@dataclass
class EffectOp:
"""效应操作"""
effect: str
name: str
param_type: 'Type'
return_type: 'Type'
@dataclass
class EffectSet:
"""效应集"""
effects: Set[str] = field(default_factory=set)
def union(self, other: 'EffectSet') -> 'EffectSet':
return EffectSet(self.effects | other.effects)
def empty(self) -> bool:
return len(self.effects) == 0
@dataclass
class EffectType:
"""带有效应的类型"""
result: 'Type'
effects: EffectSet
@dataclass
class Type:
"""基本类型"""
pass
@dataclass
class TInt(Type):
pass
@dataclass
class TString(Type):
pass
@dataclass
class TFun(Type):
param: Type
result: EffectType # 函数具有效应类型
class EffectChecker:
"""检查效应类型"""
def __init__(self):
self.effect_sigs: Dict[str, Dict[str, EffectOp]] = {}
self.handlers: Dict[str, EffectType] = {}
def declare_effect(self, name: str, ops: Dict[str, EffectOp]):
"""声明效应签名"""
self.effect_sigs[name] = ops
def check(self, expr: 'Expr', env: Dict) -> EffectType:
"""检查表达式的效应类型"""
match expr:
case Const(n):
return EffectType(TInt(), EffectSet())
case Var(x):
return env[x]
case Lambda(x, body):
body_type = self.check(body, {**env, x: EffectType(TInt(), EffectSet())})
return EffectType(
TFun(TInt(), body_type),
body_type.effects
)
case App(func, arg):
func_type = self.check(func, env)
arg_type = self.check(arg, env)
if isinstance(func_type.result, TFun):
# 检查效应处理
return func_type.result.result
raise TypeError("不是函数")
case Perform(effect, op, arg):
# 执行效应操作
if effect in self.effect_sigs:
op_sig = self.effect_sigs[effect].get(op)
if op_sig:
return EffectType(
op_sig.return_type,
EffectSet({effect})
)
raise TypeError(f"未知的效应操作: {effect}.{op}")
case Handle(body, handler):
# 处理body中的效应
body_type = self.check(body, env)
# 检查处理器覆盖效应
uncovered = body_type.effects.effects - set(handler.effects.keys())
if uncovered:
raise TypeError(f"未处理的效应: {uncovered}")
# 处理器产生纯净结果
return EffectType(handler.return_type, EffectSet())
def infer(self, expr: 'Expr', env: Dict) -> EffectType:
"""推断效应类型"""
# 类似于检查但带有推断
return self.check(expr, env)
# 示例:状态效应
def state_effect_example():
"""
effect State:
get : () -> Int
put : Int -> ()
let counter =
handle
let x = perform State.get() in
perform State.put(x + 1);
x
with
return x -> x
| get(k) -> k(0) -- 处理器提供状态
| put(n) -> k(()) -- 处理器继续
Type: () => {State}
"""
pass
效应处理器
@dataclass
class Handler:
"""效应处理器"""
effect: str
return_clause: 'Clause' # return x => expr
ops: Dict[str, 'Clause'] # op(k) => expr
class HandlerTypechecker:
"""类型检查处理器"""
def check_handler(self, handler: Handler, eff_type: EffectType) -> Type:
"""检查处理器类型"""
# 检查返回子句类型匹配
# 检查每个操作处理器类型
for op_name, clause in handler.ops.items():
# op(k) => body
# k: T -> U 其中 T 是操作参数类型
# body 必须产生 U
pass
return handler.return_clause.result_type
关键概念
| 概念 |
描述 |
| 效应 |
可观察的计算 |
| 处理器 |
处理效应操作 |
| 操作 |
效应原语 |
| 恢复 |
继续计算 |
效应推断
class EffectInference:
"""推断效应"""
def infer_effects(self, expr: 'Expr') -> EffectSet:
"""推断效应集"""
# 收集效应
effects = set()
def visit(e):
if isinstance(e, Perform):
effects.add(e.effect)
for child in e.children:
visit(child)
visit(expr)
return EffectSet(effects)
技巧
- 声明效应签名
- 处理所有效应
- 考虑多态性
- 使用处理器进行I/O
相关技能
type-inference-engine - 类型推断
linear-type-implementer - 线性类型
effect-type-system - 效应
权威参考
| 参考 |
重要性 |
| Plotkin & Pretnar, “Handlers of Algebraic Effects” (ESOP 2009) |
效应处理器的基础论文 |
| Bauer & Pretnar, “Programming with Algebraic Effects and Handlers” (J. LAMP, 2015) |
效应处理器教程及Eff语言 |
| Kammar, Lindley, Oury, “Handlers in Action” (ICFP 2013) |
推广了处理器的实现 |
| Plotkin & Power, “Notions of Computation Determine Monads” (2002) |
效应的代数基础 |
| Leijen, “Koka: Programming with Row Polymorphic Effect Types” (MSFP 2014) |
实践效应类型语言 |
权衡和限制
效应系统的权衡
| 方法 |
优点 |
缺点 |
| 行多态性 |
灵活 |
复杂推断 |
| 效应订阅 |
显式 |
冗长 |
| 隐式效应 |
方便 |
难以跟踪 |
何时不使用效应类型
- 简单程序: 单子可能更简单
- IO密集型代码: 直接IO可能更清晰
- 教学: 从单子开始
复杂度考虑
- 效应推断: 可能复杂
- 处理器组合: 非平凡
- 多态性: 需要小心
限制
- 推断困难: 效应推断难
- 处理器复杂度: 多个处理器复杂
- 错误消息: 难以清晰
- 性能: 处理器开销
- 与异常交互: 复杂
- 调试: 难以追踪效应
- 工具支持: IDE支持有限
研究工具与成果
真实世界的效应类型系统:
| 工具 |
重要性 |
| Koka |
生产效应类型语言 |
| Frank |
带推断的效应处理器 |
| Eff |
效应语言研究 |
| Multicore OCaml |
实验效应 |
| Haskell |
单子变换器处理效应 |
著名效应系统
- Koka: 微软效应语言
- Frank: (handler f) -> T 语法
研究前沿
当前效应系统研究:
| 方向 |
关键论文 |
挑战 |
| 效应行 |
“Row Polymorphism” |
可扩展效应 |
| 效应处理器 |
“Handlers of Algebraic Effects” |
处理器组合 |
| 效应推断 |
“Inference for Effects” |
自动化推断 |
热门话题
- Rust中的效应: 效应RFC
- Python中的效应: PEP提案
实现陷阱
常见效应系统错误:
| 陷阱 |
真实示例 |
预防措施 |
| 效应爆炸 |
推断出过多效应 |
多态性 |
| 错误的处理器 |
处理了错误效应 |
处理器排序 |
| 子效应 |
错误的子效应关系 |
检查 行 |