凌晨两点多,我还在改那份 SKILL.md。第 8 版。
现在上面有 61 行是“这个不要做、那个不要做”,Do NOT 开头的一共 41 条。同桌每次启动得从头读一遍,读完该干嘛干嘛。我改规则改到两点半,它跑起来跟第三版没有任何区别。
没通。就这样。
那两小时花在哪了?我到现在也说不太清楚,反正每一版都是加两条、删两条,写完觉得自己严谨了不少,一跑——
算了。
刷到那篇文章的时候我正卡在这一步。Randal Schwartz 写的,45 年从业那位。里面有个细节我盯着看了很久:他说现在的 SKILL.md,顶上五十来行是声明式的 Make 规则,底下堆了一百三十多行“Do NOT…”,已经膨胀成一份法律合同。
我笑出声。一百三十行我还没到,按比例先缩了个水。
笑完往下翻,翻到评论区,笑不出来了。
有个叫 howcani 的人,拿一份能复现的测试指出来:你算了校验值,但校验值没接进最终判定,记录而未接线,异常照样绿灯放行。
“记录而未接线”。
这六个字我在那行上停了有一分钟。不是没读懂,是读得太懂了。
因为我干的就是这个。上个月给那个小项目加保存逻辑,我写的是:
def save_rows(rows):
ok = all(len(r) == 5 for r in rows)
print(f"checksum: {ok}")
# TODO: 之后把这里接上
db.insert(rows)
return {"status": "done"}
当时我觉得挺完整——校验有了,日志有了,返回值也给了。现在看,ok 这个变量除了被打印出来,没有参与任何一条判断。行长度不对照样 insert,返回的还是 done。
它就是装饰。一个让我觉得自己很严谨的装饰。
那 41 条 Do NOT,本质上也是 41 个我算出来但没有接线的校验值。我写了“不要删测试里的 assert 来让它过”,可我没有任何一个地方在合并前真的去数 assert 有没有变少。我写了“改 schema 前先读 load_context”,但没有哪个目标的完成条件里带着“读没读”。
全是声明,没有一条是判定。
我现在有个越来越强的感觉,说出来可能有点武断:一个人写的规则文件里 Do NOT 的条数,跟他对自己代码的信心是成反比的。我 41 条的时候,其实是我最不知道自己写的东西对不对的时候。
但话说回来,这个判断我也没什么证据,就是自己身上对上了一次。刚才还觉得自己抓住了点什么,写到这儿又开始心虚——会不会只是我想给删规则找个台阶下?
文章里还有一段更狠的。
作者 1990 年 10 月开着一架可收放起落架的 Piper Arrow,从 The Dalles 回希尔斯伯勒,五边进近,塔台让他插队直接转基准边。他答应了。于是 GUMP 检查那一步被跳过——起落架没放,机腹着陆,机场两条跑道交汇处堵了大概两个钟头。
他讲的不是“记得做检查”这种废话。他说的是:你把安全不变量绑在时序的第 k 步,任何一次快进都会绕过它;但你把它做成 land 这个目标的后向链式前置条件,怎么抄近路都绕不过去。
我看到这儿心里咯噔一下,冒出一句「这不就是我吗」。我的 41 条全是第 k 步式的——它们靠我每次记得去读。而我恰恰就是那个会抄近路的人。
插一句跑题的。
作者说他 1976 年 7 月,14 岁,在比弗顿拿到第一份有偿的编程合同,用 HP 2000 BASIC 写一个叫 TOTO 的行编辑器。浮点行号配整数归一化,这样插入或移动文本块时不用立刻重排全文。五十年后,他用同一个思路,把工作流里所有分数式步骤编号全清了。
我 14 岁在干嘛。算了,这个比较没意义,我自己都写过跟别人比是场不公平的比赛。但这段确实让我卡了一下——他 14 岁写的一个取巧,五十年后还能拿出来用。我上周改的那版规则,今天回头看已经想不起来当时在防什么了。
还有一句我到现在也没完全消化:承诺得带满足条件和截止时点。
我回想我给自己派活的方式,全是“把 xxx 做完”。什么叫做完?我说不清楚。所以同桌交回来的东西,我判断不了它到底算不算完成,只能凭感觉看它像不像。这大概就是我需要写 41 条 Do NOT 的根本原因——我没给它一个能被判定的终点,只好不停地往上堆形容词。
所以我现在试着做一件很小的事:把那 41 条里能变成判定的挑出来,剩下的删掉。
比如“不要删 assert 让它过”,改成——每次改完先数一遍测试文件里的 assert,改动后如果变少了,直接判失败,不归我“记得不记得”管。
before=$(grep -c "assert" tests/*.py)
# ...改完...
after=$(grep -c "assert" tests/*.py)
[ "$after" -lt "$before" ] && echo "assert 少了,停下" && exit 1
很糙,糙到我不敢贴给别人看。但它至少不是装饰了——它真拦在那儿,我快进也绕不过去。
41 条删到 9 条。4 条变成了这种拦路的检查,剩下 5 条还是 Do NOT,我暂时想不出怎么把它们变成判定,就先留着,不硬凑。
至于文章真正核心那套东西——Flores 的行动对话、Belnap 的四值逻辑、Hoare 的 CSP 前缀,说实话我读得一知半解。尤其“执行者在语法上没有资格发出声明”这句,我大概能感觉到它在说什么,但要我讲给别人听,讲不出来。按我自己那条标准,讲不出来就是没学会,哪怕我那一刻觉得“好像懂了”。
不过“好像懂了”和“真懂了”中间那条线,我到现在也分不太清。这个词是不是大家都知道,就我一个人卡在这?
评论区蹲一个大佬,谁真读过 Flores 跟 Winograd 那本《Understanding Computers and Cognition》的,能不能告诉我“承诺”和“声明”在实际操作里到底怎么分?看作者的例子我好像懂,一合上书又糊涂了。
进度条:删掉 32 条规则,换成 4 个真能拦路的检查。不多,但这次是真接上线了。
