第二面:完成性与过程(点、线、结果)

同一条数据库迁移,同一个太平洋时间凌晨 2:14,英文为什么要给它三种不同的动词形状?中文一个”正”字就够的地方,英文凭什么强制?


开场:中文都译成”迁移出了问题”,英文为什么要分两种?

卡点 1(二选一型)。

先别急着往下看,也别急着翻下文的分析。下面这对句子,你自己先猜三秒——它们到底差在哪?答不上来也算一种答案,那恰好说明你的中文直觉正在替你偷懒。

a. The migration locked the orders table.
b. The migration was still running when Priya joined.

中文呢,几乎一模一样:

  • a. 迁移锁住了订单表。
  • b. Priya 上线时迁移还在跑。

你看,中文的区别在哪?在**“还”**这个字上。多写一个”还”,或者干脆不写——“Priya 上线时迁移在跑”——意思也不跑偏,读者自己会脑补出”进行中”。

那问题来了。

如果中文加一个”还”字或者”正”字就能搞定,英文为什么要专门造一整套 be + doing 的形态? 更狠的是:为什么这套形态在母语者耳朵里是强制的?

试试这个:

*The migration locked the orders table when Priya joined.

这句话语法上没毛病,时态对,主谓一致也对,但英语母语者听了会皱眉。不是”错了”,是画面的时间关系乱了:locked 读到耳朵里是一个已经结束的动作(锁已经拿住了,这件事已经画上句号),结果你后面跟一句 when Priya joined——你是想说 Priya 撞进了”已经完结的锁表”?那她撞了个寂寞啊。凌晨 2:44 她上来的时候,锁早就在那儿了,她”撞”进去的其实是那条还在跑的 ALTER。

有意思的是,换个连词就顺了:

The migration locked the orders table, but Priya killed the connection at 2:47.

but 把两件事排成前后:迁移先锁了表,三十三分钟后 Priya 把连接干掉了。而 when 要求的是”一件事撞进另一件事的中间”——既然要撞进中间,那被撞的那件事必须得有中间。

这就是本章的全部起点:

英文的动词形态里,焊着一个”这个事件是有中间、还是没中间”的开关。中文把这个开关放在词汇里(“正""在""过”),英文把它焊在动词骨头上——你不用也得用。

上一面(第一面:时间坐标)我们处理的是”把事件钉在时间轴的哪一格”——那是时(tense)的活儿,配上一个正交的体(aspect)轴,排出一个 12 格矩阵。那个矩阵解决的是”什么时候”。

这一面我们只盯着一件事:同一个时间点上,你把这个事件看成”点”还是”线”,看它的结果还是看它的过程。

时和体是正交的(详见第一面)。这一章我们几乎不谈”什么时候”,全谈”什么形状”。

回扣卡点 1。 你刚才要是答”a 说的是结果,b 说的是过程”——对,但那只答了一半,而且这一半是靠中文翻译猜出来的,你并不知道英文凭什么。真正的判据在这儿:a 里的 locked 把整件事封装成了一个点,点没有中间,所以后面挂不上”撞进中间”的 when;b 里的 was running 把事件拉成了一条线,线有中间,Priya 才有地方可撞。形状决定搭配,不是词汇决定搭配。 这一章就是把”形状”这两个字一层层拆给你看。

好,那”形状”是什么?我们得先从动词本身说起。


一、点 vs 线:动词自带的”形状”

你可能一直以为”点”和”线”是时态给的。不是。动词自己就带形状,时态只是决定这个形状怎么被剪辑。

语言学界管这个叫词汇体(lexical aspect,也常叫 Aktionsart,德语,字面意思就是”动作的种类”)。注意它跟”语法体”(grammatical aspect,也就是 be doing / have done 这些形态)是两码事,但两者天天打架,打出来的结果就是你脑袋里那堆”说不清为什么就是怪”的句子。

卡点 2(判断对错型)。

讲规则之前,先做一件小事:给下面十个动词分两堆——“一瞬间就完的”和”能持续一会儿的”。

lock, notice, run, kill, blink, sleep, arrive, rain, own, walk

先别往下看,真的在心里分一遍。我猜你分得很快,快到有点心虚。

分完了?对答案。前九个大概没什么争议:blink notice kill arrive 是点,run sleep rain walk 是线,own 是状态(这个等下再说)。你八成分对了,而且分得毫不费力。

那我问你:lock 你放哪一堆了?

你犹豫过。而且你犹豫得有道理——因为你隐约记得两句话好像都能说:

a. The migration locked the orders table at 2:14.   (锁在 2:14 那一刻拿到)
b. The orders table was locked for 33 minutes.      (锁这个状态持续了 33 分钟)

a 里的 lock 明明是一瞬间的事——元数据锁申请到了,完事,可以钉在一个坐标上。b 里它又明明挂了半个多小时。同一个词,一会儿点一会儿线?

打脸时刻。 你八成会想说:“哦,那说明 lock 是个两可的词,看语境。” 对,但那只答了一半,而且答的是最没用的那一半——“看语境”这三个字等于没说。真正的原因是:你把两个不同的东西当成了一个。

  • lock 这个动作是点。它有一个确切的时刻:2:14。
  • lock 这个动作造成的结果状态是线。它有一段长度:33 分钟。

英文把这两件事分给了两个不同的主语:动作归 the migration,状态归 the orders table。中文里”锁”一个字全包了,所以”锁了三十三分钟”在中文里天然是歧义的——到底是”锁这个动作花了三十三分钟”(荒谬),还是”锁住的状态持续了三十三分钟”(正常)?中文靠你脑补,而脑补从来不出错,因为没人会理解成前者。

英文不许你歧义。它逼你选主语:选 the migration,你就在说动作(点);选 the orders table,你就在说状态(线)。

类比:defer 一个函数。调用的时刻是一个点;它安排的那个效果会一直挂到你 return。你不能说”这个函数 defer 了三十三分钟”,你得说”它 defer 出去的那件事,挂了三十三分钟”。动作和结果,永远是两个东西。

回扣卡点 2。 你刚才在 lock 上犹豫,不是因为你分类能力差,是因为你脑子里那个中文的”锁”字把动作和结果焊死了,你从来没被逼着把它们拆开过。而这一章要教你的第一件事,就是怎么拆。

好,正式开始。

1.1 第一层:瞬间 vs 持续

最直观的一刀,把动词切成两类:

类型中文例子特征
点动词(punctual)瞬间动词lock, notice, wake up, escalate, kill, spot, blink, sneeze, die一瞬间就完,没有可展开的中间
线动词(durative)持续动词run, climb, try, retry, wait, watch, sleep, work, read, rain有时间长度,中间可以被切开

先别背表,感受一下。母案例里有的是现成的材料:

a. Lin noticed the alert at 2:26.
b. *Lin was noticing the alert at 2:26.

b 怪不怪?怪。notice(注意到)是一个认知上的”啪”——上一秒他还在睡,下一秒手机屏幕的光打在脸上,他看见了。中间是空的。你没法进入一个宽度为 0 的东西内部。 这就是点动词的死穴。

再来一个:

a. The alert escalated to Priya at 2:44.
b. *The alert was escalating to Priya at 2:44.

escalate(升级)在告警系统的语境里就是一个离散事件:PagerDuty 的策略跑完,铃转到下一个人手里,完事。它是状态机的一次跃迁,不是一段过程。

但线动词完全没问题:

a. The error rate climbed for twenty-three minutes.
b. The error rate was climbing.

两句都顺,只是画面不同。climb(攀升)有长度,你可以切进它的任何一刻。

类比时间。 你熟悉这个:

  • 点动词 = 一次原子操作(atomic operation)。要么没发生,要么已经完成,中间不可观测。就像一次 atomic.StoreInt32——你永远抓不到它”存到一半”的状态。
  • 线动词 = 一段有执行时间的过程,中间有可观测的状态。像个跑了 200ms 的函数,你可以在中间打断点。

进行体(be doing)的作用是”进入事件内部拍过程”。点动词内部是空的,你进不去。所以 *Lin was noticing、*The alert was escalating 天然别扭。

一个必须单独拎出来的词:lock

母案例的核心动词 lock 特别有意思,因为它同时踩在两类上,而正是这个”踩界”造成了它后来的各种用法。

a. The migration locked the orders table at 2:14.        (点:锁在 2:14 那一刻申请到)
b. The orders table was locked for thirty-three minutes. (状态:锁持续了 33 分钟)
c. *The migration was locking the orders table.          (怪:你没法"正在锁")

a 里的 lock 是点——元数据锁在某一瞬间拿到,这是一个瞬时事件,可以钉在 2:14 这一个坐标上。

b 里的 locked 是状态——锁拿到之后就一直挂着,直到 2:47 Priya KILL 掉那条连接。这里的 33 分钟不是”锁这个动作”的长度,是”锁住这个结果”的长度。

c 崩,因为你把点和状态混在了一起:was locking 试图进入那个瞬时事件的”内部”,而内部是空的。

这就是点动词最容易被忽略的一条性质:一个瞬时动作,可以产生一个持续的结果。 动作是点,结果是线。英文把这两件事分工得很清楚——lock 管动作(点),be locked 管结果状态(线)。中文里”锁住”一个词全包了,所以你写英文的时候最容易在这里翻车:你想说”锁了 33 分钟”,你不能说 *The migration locked the table for 33 minutes,你得让状态出场。

记一句:

动作的长度和结果的长度,在英文里是两个东西。 中文的”锁了三十三分钟”是歧义的(到底锁这个动作花了三十三分钟,还是锁住的状态持续了三十三分钟?靠脑补);英文不许你歧义,它逼你选主语——选 the migration 就是动作,选 the orders table 就是状态。

1.2 这里有个陷阱:die 到底是不是点?

卡点 3(判断对错型)。

先做个判断题:He was dying. 这句话成立吗?

先别往下看,先答。我猜你八成会说”不成立”——因为上一节刚讲完”点动词进不去进行体”。这个答案错了,而且它错得特别有价值。

你八成会举手:等等,He was dying 明明很常见啊!

对,非常常见。这不矛盾吗?

不矛盾,而且这个例子恰好能让你看清机制:die(死)这个词是点,但”濒死”这件事是一条线。英语很聪明,它用 be dying 把镜头对准的其实不是”死”那个瞬间,而是死之前那段过程。所以 He was dying 真正的意思是”他快死了 / 他正在濒死”,根本不是”他正在死”。

母案例里就有现成的一句,运维同学听了会心一笑:

The Order API was dying.

不是说它已经死了(那叫 was down),是说它正在垂死挣扎:p99 在涨,连接池在积,超时在堆积,但进程还活着。这句的恐怖程度比 The Order API was down. 高得多——后者是结论,前者是过程,而过程里才有那种”眼睁睁看着它滑下去”的无力感。

同样的:

a. *He was sneezing.          (一次喷嚏:怪)
b. He was sneezing.            (连打一串喷嚏:完全正常)

b 之所以成立,是因为”打喷嚏”被复数化了——一串喷嚏构成了一条线,你才能进去。这就是语言学家说的事件复数化(event pluralization):把一个点复制 N 份,点就变成了线。

The gun was firing.(枪在连发)成立,就是同一个道理。母案例里也有一句天然的复数化:

Orders were failing.

一笔订单失败是点(An order failed.),但 12,300 笔订单在 51 分钟里连续失败——这是一条由一万两千个点铺成的线。所以 Orders were failing 顺得不能再顺,而且它自动带出了”那段时间里持续在发生”的画面。

这一条很有用,记下来:点动词不是”绝对不能用进行体”,而是”必须先用别的方式把它变成线”——要么改指它前面的过程(be dying),要么把它复制成一串(be sneezing / be firing / orders were failing)。

回扣卡点 3。 你刚才答错,是因为你把”点动词不能进进行体”记成了一张词汇清单——“这几个词不许加 -ing”。它不是清单,它是一条几何约束:进行体要的是”有内部可看的线”。所以救活一个点动词的路永远只有两条:要么把镜头往前挪,去拍它到达之前的那段过程(was dying = 濒死,不是”正在死”);要么把它复制成一串,让一串点连成一条线(orders were failing = 一万两千笔订单在 51 分钟里连续失败)。你不是在背单词,你是在给镜头找一段能推进去的画面。 只要找得到,任何点动词都能进进行体——而母案例里 The Order API was dying. 之所以比 was down 可怕十倍,就是因为它拍到了那段过程。

卡点 4(二选一型)。

下面两句只差三个词。请回答:哪一句能配上”走了十分钟还在走”?

a. She walked for ten minutes.
b. She walked to the car for ten minutes.

先别往下看,在心里答一遍,最好出声答一遍。

打脸时刻。 八成你会说”a 能,b 不能,因为 b 加了 to the car,走到车那儿就结束了,没法’还在走’“。前半句对,后半句错了——而且这个错误特别顽固。

真相是:两句都能说”走了十分钟还在走”,但只有 b 里的那句”还在走”是有分量的。 因为 b 有终点,for ten minutes 说明十分钟后终点还没达到——她还没走到车那儿。 而 a 没有终点,“还在走”只是一个无信息量的默认状态:她不走还能干嘛?

所以更精确的说法是:

c. She walked to the car in ten minutes.       (她十分钟走到了车那儿:到了)
d. She walked to the car for ten minutes.      (她朝车走了十分钟:没到,或者你可能不知道她到没到)

加了三个词,“到达”就没了。 为什么?因为你加的不是三个词,是一个终止条件。

机制是这样的:walk 无界(atelic)——走十步是走,走十公里也是走,你随时叫停,“走”都成立,切掉任何一段剩下的还是”走”。walk to the car 有界(telic)——车是终点,碰到它这件事就”满了”,再往前走就不属于”走向那辆车”这个事件了。

Go 类比,你天天写:

  • 无界 = for { ... } —— 没有终止条件的循环,跑一毫秒是它,跑一万年也是它。
  • 有界 = for i := 0; i < n; i++ —— 有 i < n,跑到 i == n 就叫”跑完了”。

to the car 就是那个 i < n。 所以这里必须记住一句会贯穿全章的话:

有界性不是动词的属性,是”动词 + 它的论元”这整个短语的属性。 walk 还是那个 walk,变的是你给它配的宾语和方向短语。

回扣卡点 4。 你刚才之所以在 b 上犹豫(或答错),是因为你脑子里有一条没说出来但深信不疑的假设:“有终点 = 一定到达”。语言的实际规则恰恰相反:有终点 + for = 停在终点之前。 终点的作用不是保证到达,是让你能测出没到达。

这条规则马上就要在母案例上炸开——Lin tried to kill the migration for ten minutes,那个”他没成功”的意思,一个字都没写,全是这套机制算出来的。

1.3 第二层(更重要):有界 vs 无界

好,现在来真正狠的一层——有界性(telicity)。

先看这一对,感受差别:

a. The error rate climbed.
b. The error rate climbed to 34%.

两句都是 climb,但 a 和 b 给你的”完成感”完全不同。为什么?

因为 climb 没有内在终点。涨到 5% 是涨,涨到 34% 也是涨,你随时叫停,这一”涨”都成立。而 climb to 34% 有一个内在的终点:那条 34% 的线。碰到它,这个事件就”满了”,再多涨一点都不算这个事件的一部分。

语言学里:

  • 无界(atelic):climb、run、rain、watch a dashboard、retry queries —— 没有内在终点,可以无限延伸,切掉任何一段剩下的还是同一个东西。
  • 有界(telic):climb to 34%、run the migration to completion、kill the connection、send the customer notice —— 有内在终点,到了终点事件就”完成”了,不到终点事件就”没完成”。

Go 类比(这个你一定秒懂)

  • 无界 = for { ... } 或者 for { ch <- heartbeat } —— 一个没有终止条件的循环。你跑一毫秒是它,跑一万年也是它,循环本身不承诺任何”完成”。
  • 有界 = for i := 0; i < n; i++ —— 有明确的终止条件 i < n。跑到 i == n 就叫”跑完了”,没跑到就叫”没跑完”。

climb 是 for {},climb to 34% 是 for i < n。把宾语或方向短语加上去,就等于给循环加了个终止条件。 这就是 telicity 的本质:不是动词单独的属性,是”动词 + 它的论元”这整个短语的属性。

母案例里最漂亮的一组对比在这里:

a. Lin ran the migration.              (无界:他跑了那条迁移。跑了多久?跑完没有?都不承诺)
b. Lin finished the migration.         (有界:finish 自带终点——到终点才算 finish)
c. Priya killed the connection.         (有界:kill 的终点就是那条连接断掉的一瞬)

b 和 c 为什么天然是”点”?因为 finish 和 kill 这两个词的词义本身就包含了终点——你没法”finish 了一半”。语言学家管这类叫成就动词(achievement):瞬间的、有界的、一到终点就不可逆。

而 run 是活动动词(activity):无界,可以停在任何地方。所以母案例里那句 The migration was still running 之所以成立、之所以让人揪心,正因为 run 无界——它停在哪儿都行,包括停在一个谁都不想要的地方。

这一点中文也有痕迹,但中文不强制你标记它,英文强制。 你说”他跑了那条迁移”,中文读者要靠上下文才知道跑完没跑完;英文里 ran 和 finished 是两个词,逼你表态。

1.4 一个免费的体检工具:in vs for

卡点 5(二选一型)。

换一个跟代码完全无关的现场。去年冬天,你家热水器坏了,你叫了个师傅。下面两句,哪一句告诉你热水器修好了?

a. The plumber fixed the water heater in an hour.
b. The plumber worked on the water heater for an hour.

先别往下看。

打脸时刻。 你选 a,对。但我要追问:为什么 b 不能告诉你修没修好? 你八成会说”因为 b 没说结果”——这是把结论重复了一遍,不是解释。

解释是这样的:fix the water heater 是一个有界短语,它的终点就是”修好、能出热水了”。有界短语才答得上来”in + 一段时间”(跑完花了多久)。所以 a 一旦用了 in,就承诺了终点被到达。

而 work on the water heater 无界——“鼓捣”这事儿没有内在终点,鼓捣十分钟是鼓捣,鼓捣一天也是鼓捣。它只能配 for(持续了多久),而 for 只承诺过程在这段时间里展开,不承诺终止条件被满足。所以 b 的听者会默认:一个小时后,这事儿还悬着。

更狠的是:如果 b 出现在你跟朋友的抱怨里(The guy worked on it for a whole hour and then said he'd have to come back.),那个”没修好”根本不是后半个句子说出来的——for an hour 加上 work on 已经说完了,后半句只是补充。

回扣卡点 5。 你刚才凭直觉选对了,但你凭的那个直觉,是把 in 和 for 当成”在……之内”和”持续”两个普通介词。它们不是。它们是一对有界性探针:往句子里一插,出口的值告诉你这个短语有没有终点。in 通过 = 有终点且已到达;for 通过 = 不承诺终点。 这是本章最好用的一个随身工具,因为它是唯一一个”不用懂理论也能立刻用”的检测手段。

有界无界这么抽象,有没有一行命令就能测的?有。语言学里经典的 in/for 测试,我给你翻译成 Go 工程师的版本:

  • in + 一段时间 = 这个事件”跑完”花了多久(耗时,time-to-completion)。只有有界短语能过。
  • for + 一段时间 = 这个状态”持续”了多久(时长,duration)。两种都能过,但味道不同。

上手试,全部取自 11 月 12 日凌晨那条时间线:

a. Lin traced it to the migration in five minutes.       (有界:2:26 醒来,2:31 定位到了)
b. *Lin searched the logs in five minutes.               (无界:怪。除非读作"五分钟后他开始翻日志")
c. Lin searched the logs for five minutes.               (无界:翻了五分钟日志。没说翻出什么)
d. Lin tried to kill the migration for ten minutes.      (有界 + for:他试了十分钟——暗示没成)

b 为什么怪?因为 search the logs 没有”跑完”这个概念,你问”他翻完日志花了五分钟”,答不上来——翻到什么算完?

d 是全组里最有意思的一句,你要仔细品:一个本来有界的短语(kill the migration),一旦配上 for,反而暗示它没到终点。 因为 for 只承诺”这段时间里过程在展开”,不承诺”终止条件被满足”。

Lin tried to kill the migration for ten minutes, then he escalated to Priya.

读到这句,你自动脑补出:他没成功。而:

Priya killed the migration in under a minute.

这句你脑补的是:她成了。

同样一个”中止”,中文全都是”他试着把迁移停掉,试了十分钟”——英文靠 in / for 这两个介词,把”终点达没达成”焊进了句子里。 这就是全书的那个核心观察:中文靠词汇和上下文,英文靠形态。介词在这里干的就是形态的活。

再补一刀,这次是整场事故的收口:

a. The service recovered in fifty-one minutes.     (有界:2:14 到 3:05,跑完这个恢复流程花了 51 分钟)
b. The service was recovering for fifty-one minutes. (无界视角:那 51 分钟里它一直在恢复中)

a 是事故报告里的那句数字,b 是那种你在复盘会上不愿回忆的、熬人的过程。同一个 51 分钟,一个是 KPI,一个是体感。

1.5 有界性为什么重要?因为它决定了”完成”是什么意思

到这里,你能回答一个一直含混的问题了:“完成”到底是完成了什么?

答案:只有有界事件才谈得上”完成”。

a. Lin has worked on the Order API.               ->   他做过 Order API(人生经验:他干过这活儿)
b. Lin has written the migration runbook section. ->   他把那段 runbook 写好了(结果:现在文档里有这一节)

a 里的”完成”其实是”发生过”(经历),b 里的”完成”才是”做完了”(结果)。区别从哪来?从 the migration runbook section 这个定指单数宾语带来的天然边界——一节写完就完。

再推一步,一条铁律:

完成体(have done)对无界短语只能给”经历过”或”持续了多久”,给不出”做完了”。要做”做完了”,你的短语必须自带终点。

a. Priya has worked at Pallet.                        (她干过 / 她一直在干)
b. Priya has worked at Pallet for eight years.        (持续:八年,还在干)
c. Priya has read the incident report.                (结果:她看过那份报告了,现在她知道里面写了什么)
d. Priya has read incident report after incident report. (经历 / 持续:她看过一份又一份)

c 之所以是”结果”,是因为 the incident report 是定指的单数个体,有天然边界(一份看完就完)。d 把宾语复数化了,边界消失,于是从”结果”滑向了”经历”。

这一节的落点: 动词不是一团模糊的”动作”,它是带形状的实体——有点有线,有界无界。语法体(进行、完成)不是凭空施加的魔法,它是在这些形状上做手术。手术能不能做,得看形状允不允许。

1.6 跨领域验证:厨房、赛道、机房,同一组形状

前面讲的全是机房里的事。你心里可能已经冒出一句怀疑:点 / 线 / 结果这套东西,会不会是数据库迁移这种场景刚好特殊?

不是。我把同一组形状扔到两个跟代码毫无关系的现场,你看它照不照样成立。

现场一:厨房,做饭烧糊(生活)

上周三晚上七点半,你在家做番茄牛腩,顺手把米饭坐上灶。手机响了,一个临时的会议邀请,你点了接受,戴着耳机进了书房。十九分钟后你回到厨房,锅底那层已经黑了,抽油烟机在响。

a. The rice burned at 7:49.                          (点:burn 是成就动词,糊掉是瞬间不可逆的)
b. The rice was burning while I was on the call.     (线:镜头推进"正在糊"的内部)
c. The rice was burnt when I got back.               (结果状态:你回来时它已经是糊的了)
d. *The rice was burning for nineteen minutes.       (别扭:见下)

a 和 c 的差别,就是 1.1 那条”动作是点、结果是线”的翻版:burn(糊掉的那一刻)是点,be burnt(糊了这个状态)是线。中文一句”饭糊了”两个都包了,英文不许你混——所以你没法说 *The rice burned for nineteen minutes.(“糊”这个动作不可能持续十九分钟),你得让状态出场:The rice was burnt for nineteen minutes before anyone noticed.

d 为什么别扭?因为”正在糊”这个过程在正常语境下不可观测——你人在书房开会,没在看它。要让 d 那一类句子成立,你得给它可观测性和长度:

The rice had been burning for ten minutes before I smelled it.
(在你闻到味儿之前,它已经烧了十分钟了。)

这就是完成进行体:过程(在烧)+ 长度(十分钟)+ 痕迹(味儿)。三件事一个形态给全了。

这是点 / 线 / 结果这组概念的第一个非技术证据:它不是 DBA 的行话,它就在你的厨房里。

现场二:赛道,马拉松跑崩(运动)

2025 年 10 月,北马,气温 21 度。你前半程配速稳在 5:10,到 32 公里开始掉速,35 公里小腿抽筋,最后 7 公里走了将近 4 公里,净成绩 4 小时 26 分。

a. His legs gave out at 35 kilometers.                    (点:give out 是成就动词,腿"罢工"是一瞬间)
b. He was still running at 30 kilometers.                 (线:镜头停在 30 公里处,终点还不可见)
c. His legs were shaking for an hour after the finish.    (结果状态:线,长度一小时)
d. He had been running for three hours when his legs gave out.  (完成进行:线 + 尾巴)

a 和 c 又是一次”点 vs 结果状态”:抽筋(give out)是一瞬,抖(be shaking)是结果状态,持续了一小时。所以你不能说 *His legs gave out for an hour.——腿不可能”罢工”整整一小时,罢工完就该抖了。中文说”他腿抽了一个小时”听着毫无问题;英文会逼你分清:抽是点,抖是线。

d 是这一小节的收尾。它把三个小时这条线,直接怼到”腿罢工”那个点上。整句没有一个形容词,但任何跑过全马的人读完都会膝盖一软。

三个现场,一张对照表

现场点(动作 / 瞬间)线(过程)结果状态(线,长度另算)
机房locked the table at 2:14was runningwas locked for 33 minutes
厨房burned at 7:49was burningwas burnt when I got back
赛道gave out at 35 kmwas runningwas shaking for an hour

三个领域,三种完全不同的物理过程,同一组形状。 所以这不是”英语在数据库场景里的特殊规定”,这是英语看待一切事件的方式。

再注意一个共同点:三个现场里最让人难受的那一句,全都是完成进行体。 机房里的 had been trying to kill it for ten minutes,厨房里的 had been burning for ten minutes before I smelled it,赛道上的 had been running for three hours when his legs gave out。因为只有完成进行体同时给你长度和代价。这件事第六节专门拆。

那手术是什么?下一节,本章的核心模型。


二、摄像机比喻:体的本质是一次机位调度

卡点 6(翻译缺口型)。

先做一个跟机房无关的实验。去年夏天某个晚上,你们小区停电。下面三句话,描述的是同一个晚上、同一件事。请你读完后回答一个问题:这三句话在你脑子里唤起的画面,是一样的吗?

a. The power went out at 8:03 p.m.
b. The power was still out when I got home at 9:40.
c. The building had been running on the backup generator for two hours.

先别往下看。三秒。

画面当然不一样。 a 是一行记录(八点零三分,啪,没了);b 是你站在楼道里摸黑掏钥匙,空气里有一股电梯停运后的闷味;c 是柴油发电机在楼下嗡嗡响了两小时,你开始算它还能撑多久。

现在关键问题来了:这个差别,是英文制造的,还是你自己脑补的?

打脸时刻。 八成你会说”是我自己脑补的,因为我知道停电是怎么回事”。错。 做个对照实验就知道了——把三句翻成中文,然后把中文里那些补信息的词全删掉:

  • a. 晚上八点零三分停电了。
  • b. 我到家时停电。(删掉”还”)
  • c. 这栋楼用备用发电机两小时。(删掉”撑了”)

删完再看:三句画面坍缩成一团,你分不出谁是谁了。

这就证明了:英文那三句的差别不在词汇里,在形态里。 中文也能造出同样的差别,但中文是用”还""撑了""正在”这些词临时加上去的——加了就有,不加就没,读者靠脑补。英文是焊在动词上的:你说 went out,画面就被压成了点;你说 was out,画面就被拉成了线;你说 had been running,画面就长出了一条两个小时长的尾巴。你不用也得用。

机制(下一节展开):差别不在信息量,在观察者站的位置。

  • a 的观察者站在事件外面,把整件事压成一个点;
  • b 的观察者站在事件内部,看到的是一段还在展开的状态,并且它撞上了”我到家”这个点;
  • c 的观察者站在”两小时后”往回看,看到的是一条有长度的线,而且这条线还留着痕迹(发电机在转,油在烧)。

类比:a 是事故记录里的一行时间戳,b 是你手机拍的一段视频,c 是那段视频加上进度条上”已播放 2:00:00”的角标。同一个硬盘,三种读取方式。

回扣卡点 6。 你刚才说”是我自己脑补的”——这句话本身恰好证明了本章最要紧的那个判断:中文读者太习惯脑补了,以至于分不清”句子给的”和”自己加的”。 你在中文里从来不需要做这个区分,所以你在英文里也感觉不到自己正在被迫做选择。这一整章要做的,就是把这两样东西分开。

前面讲了”点”和”线”,但还没回答最关键的问题:谁决定把它看成点还是线?

答案是:摄像机。

这是我见过最好用的一个模型,也是全书最形象的模型之一。你把”语法体”彻底忘掉,改想成一台摄像机——因为”体”(aspect)这个词的本义,在拉丁语源里就是”看的样子”。体和视角,本来就是一回事。

摄像机有三个旋钮:

  1. 机位:架在事件外面,还是推进事件里面?
  2. 焦距:广角收全景,还是长焦推特写?
  3. 朝向:对着未来拍,还是掉头往回看?

英文的四种体,就是这三只旋钮的四种组合。

2.1 一般体:机位在事件外面,拍完整块,然后压成一个点

The migration locked the orders table.

机位架在整件事的外面,从头到尾完整入画,然后——关键动作来了——咔嚓,把整段过程压缩成一个不可分割的点。

你得到的不是一段录像,是一张成品照片。照片上的时间是凝固的:这件事发生了,句号。

所以一般体的本质动作是封装(encapsulation)。

Go 类比,非常准:

// 一般体:事务已 commit,你只关心它成功了没有,不关心内部跑了哪些语句、持锁多久
ok := runMigration(ctx, "ALTER TABLE orders ADD INDEX idx_buyer_created")
if !ok { ... }   // 一个 bool,过程被封装掉了

事件内部的时间轴被压成一个 bool。这就是”点”。

  • 一般体回答的问题是:这件事发生了吗? —— true / false
  • 一般体主动丢掉的信息是:内部结构、阶段、进展到哪儿了
  • 所以它最适合:报事实、列清单、写新闻标题、写日志摘要、写事故报告的结论行

再一个类比:一般体是 git log --oneline 里的一行。 一行就是一次 commit,你看到它”发生过”,但看不到 commit 里的 diff。

母案例里,一般体就是事故报告标题的体:

MIGRATION LOCKS ORDERS TABLE, 51-MINUTE OUTAGE

注意标题体连冠词都省了。为什么?因为标题要的就是”点”——最快的那个点,一点废话都不能有。

而且你注意到了吗:事故报告的标题里,lock 这个动作被压成点之后,51 分钟的痛苦消失了。只剩下一个数字。这就是封装的代价,也是封装的用途——报告需要一个可以归档的结论,不是一个可以回放的过程。

2.2 进行体:机位推进事件内部,拍过程,拉成一条线

The migration was still running when Priya joined.

现在摄像机推了进去。你不再站在事件外面看它的边界,你站在事件的中间。

这一推,发生了三件事:

第一,事件从点变成了线。 你看到了内部:SHOW PROCESSLIST 里那条 ALTER 挂在那儿,state 从 copying to tmp table 慢慢往前爬;连接数在涨;p99 从 200ms 爬到 2s 再往上;orders 表的写入开始排队。

第二,你看不到结尾了。 这是进行体最要命的性质,很多人没意识到:因为你站在中间,你的视野里只有”正在展开的这一段”,终点在你前方,而摄像机不朝前拍——它朝侧面拍过程。所以进行体在语义上根本不承诺终点。凌晨 2:44 那一刻,没人知道这条 ALTER 会自己跑完还是被人杀掉。

第三,也就因此,进行体天然可以被打断。 when Priya joined 之所以能插进来,就是因为你正站在中间,Priya 才能撞进来。事件如果是封装好的点,是撞不破的。

Go / 运维类比,这个是本章最贴切的一个:

# 进行体:tail -f。你看到的是流,你永远看不到 EOF
$ tail -f /var/log/order_api/error.log

tail -f 的时候,你就是在事件内部。文件可能还会继续写,也可能下一秒就没人写了——你不知道,也不该知道,因为你在这条流里。

再一个类比:进行体 = 打开一个还没写完的文件读。你读到第 300 行,这 300 行是真的,但”文件是否写完”这个信息,在你当前的位置上是不可知的。

这就埋下了本章最重要的一个结论,我先在下面划出来,后面(思考题第 5 题)我们还要回来深挖:

“可能没完成”不是进行体的副作用,是它的定义。 只要你把事件拉成线,你就必然处在”线没有终点”的状态。“未完成”是”线”这个几何形状的推论,不是额外的语义。

摄影类比再补一刀:进行体是曝光中,快门还开着,画面还在累积。一般体是快门已经闭合,一张定稿。

  • 进行体回答的问题是:那一刻,画面里在演什么?
  • 进行体主动丢掉的信息是:结局
  • 所以它最适合:描写场景、铺垫背景、给另一件事当”被撞的背景板”

母案例里,进行体就是监控面板的体。Grafana 上那条 2:14 之后开始抬头、2:37 冲到 34%、2:52 回落的曲线——它不告诉你结局,它只告诉你”此刻正在发生什么”。你看面板的时候,本质上就是在读 was running / was climbing。

2.3 完成体:机位退到结果处,掉头往回看

这是最容易被讲错的一种。绝大多数教材说”现在完成时 = 过去发生的事对现在有影响”——这话没错,但太空,太空就没法用。

摄像机的说法是这样的:

The incident has been resolved. No data was lost.

摄像机没有停在事件中间,它退了出去,一直退到”现在”这个位置,然后掉头往回看。

所以你拍到的根本不是事件本身。你拍到的是:

  1. 事件留下的痕迹(事故单状态是 RESOLVED,复盘文档归档了,三条行动项全部落地)
  2. 一条从过去通向现在的连线

这就是为什么”现在完成”叫”现在”完成——它的机位站在现在。它不是过去时,它是现在时的一种:它描述的是”现在的状态”,只不过这个状态是通过回看来解释的。

Go 类比,这个很妙:

// 完成体:你不看 diff,你看的是当前 working tree,并知道它是被某次 commit 造成的
state := repo.CurrentState()   // 现在的状态
// 完成体的语义是:这个 state 是"已经 apply 了那次 commit"的 state

再看一个更直白的:数据库 binlog 已经回放完毕。 你看到的不是回放过程,你看到的是当前表的数据,外加一条”这次变更曾经发生过”的事实。

所以完成体回答的问题不是”什么时候发生的”,而是:

现在怎么样了?以及,这个”怎么样”是从哪儿来的?

这一点极其重要,它解释了为什么完成体不能跟具体的封闭过去时间状语连用:

*Lin has run that migration on November 12, 2024.

不行——(这条判据的详细展开在第一面已经讲过,这里只点一句:本质就是机位只能有一个。)因为你的机位站在 2024 年 11 月 12 日内部,你就没法同时站在 2026 年的现在往回看。这是机位冲突,不是什么”时态搭配规则”。

母案例里这一条特别值得玩味,因为它造成了一个真实存在的表达难题:你想说”那条迁移发生在 11 月 12 日凌晨”,你只能用一般过去时;你想说”这件事到今天还有余波”,你只能用完成体。你没法一句话同时做这两件事。 英文逼你在”钉时间”和”谈影响”之间二选一,中文一句”那条 11 月 12 日的迁移到现在还有影响”两个都占了。

2.4 完成进行体:两副机位叠着开

Lin had been trying to kill it for ten minutes.

这个形态是四种里信息量最大的,因为它同时架了两台机:

  • 一台是进行体的:站在过程内部(线)
  • 一台是完成体的:站在结果处回看(尾巴)

合起来你看到的是:一条从过去某个点开始、一直延伸、一直到你当前的机位还没停(或刚停)的线,并且这条线留下了痕迹。

信息量清单:

信息谁提供的
这是一个过程,有内部进行体
这个过程持续了一段时间for 短语 + 完成进行
到参照时刻为止,它没停完成进行
它留下了可见的痕迹完成体
它不是永久状态,是一段有始有终的过程完成进行(暂时性)

Go 类比:strace 一个已经跑了三小时的进程。你既看到系统调用的流(进行),又看到它当前的累积占用(完成)。单看 ps 你只有后者,单看实时日志你只有前者。strace 给你两个。

放到母案例里:had been trying 给你的就是一次 strace——你看见他一次次敲下 KILL、一次次 SHOW PROCESSLIST、看见那条连接还在,也看见十分钟就这么没了。单看”他尝试过中止”你只有结论,单看”他在尝试”你只有此刻,had been trying 给你两个。

2.5 一张总表(本章的核心地图)

体摄像机在哪时间形状你看到你看不到形态回答的问题
一般体事件外面,拍全景点(封装)事件整块发生了内部结构、进展locked发生了吗?
进行体事件内部线(无终点)过程正在展开结局was running那一刻画面里在演什么?
完成体结果处,往回看点 + 尾巴留下的痕迹过程细节has resolved现在怎么样了?
完成进行体内部 + 结果处线 + 尾巴过程 + 时长 + 痕迹终点had been trying到那一刻为止,一直在怎样?

2.6 把同一个动词走一遍四种体

光看表格会飘。我们用一个最土的动词 run,把四种机位全走一遍,你立刻就踏实了。全部基于母案例:2024 年 11 月 12 日太平洋时间凌晨 2:14,Pallet 的后端工程师 Lin Wei 在旧金山的家里执行那条 ALTER TABLE orders ADD INDEX idx_buyer_created (buyer_id, created_at)。

a. The migration ran for thirty-three minutes.
   (一般体:点。那条迁移跑了 33 分钟——2:14 起跑,2:47 被 Priya 干掉。事实陈述,句号。)

b. The migration was still running when Priya joined.
   (进行体:线,被撞断。2:44 她上线时,那条 ALTER 还挂在那儿。跑完没跑完?不知道,也不重要——画面在"跑"的中间。)

c. Lin had run the same ALTER a dozen times in staging.
   (完成体,过去机位:经历。那天之前,他在预发环境跑过十几次一模一样的语句。留下的痕迹是"他熟"——熟到把预发的手感带进了生产。)

d. Lin had been trying to kill it for ten minutes when Priya took over.
   (完成进行体:线 + 尾巴。到 Priya 接手的那一刻为止,他已经跟它搏斗了十分钟。)

把这四句翻成中文:

  • a. 那条迁移跑了三十三分钟。
  • b. Priya 上线时,迁移还在跑。
  • c. 那天之前,他在预发环境跑过十几次同样的语句。
  • d. 到 Priya 接手时,他已经试着中止它十分钟了。

中文靠”还在""过""已经""了”这些零散的字,靠”这时""之前”这些词,靠读者脑补。英文靠动词的形态。每转一次机位,英文句子就换一副面孔,中文翻译却几乎看不出变化——这就是钻石转到第二面时给你的东西。

而且你注意 c 这句的杀伤力:它一个评价词都没有。它没说 Lin 鲁莽,没说他大意,它只是把摄像机退到 2:14 之前,让他”在预发环境跑过十几次”这条线铺在那里。剩下的判断,读者自己会做——而这正是事故复盘要的那个效果:不指责人,但把结构摆出来。(三条行动项最后定性为流程缺失而不是个人失误,靠的就是这种叙述方式。下一面讲语态与主语选择时,我们会看到更多这种”把人从句子里挪走”的手法。)

2.7 顺手说一句:机位还能再往前架

摄像机模型还有第四个位置我们这章不展开,但你值得知道它存在:机位架在事件之前,朝前拍。

a. Lin was going to run it through gh-ost.   (机位在过去某个点,朝前看:他当时打算用 gh-ost 的……)
b. This time next year, Lin will be training the new on-call rotation.   (机位在未来某个点,朝内看:到那时他会正在带新人)

was going to 之所以天然带”后来没成”的味道,也是同一条几何原理:你朝前拍,看不到终点,所以不承诺终点。 母案例里这句简直是量身定做的——gh-ost 就在工具箱里,他当时是打算用的,然后他没用。整个事故就藏在这两个形态的缝隙里。

行了。现在你有了机位模型,我们可以去收拾一批具体的”怪句子”了。下一节解决一个你肯定背过、但肯定没被讲透的规则:为什么状态动词不能用于进行体。


三、为什么状态动词不能用于进行体?

卡点 7(判断对错型)。

两道题,一起做。

a. *I am knowing the answer.
b. I am being rude.

第一问:a 为什么怪? 第二问:既然状态动词不能进进行体,那 b 里的 be 凭什么就能?同样是 be,为什么一个能进进行体一个不能?

先别往下看。这两问里,第一问你背过答案,第二问你大概率从来没想过——而第二问才是真问题。

打脸时刻。 第一问的标准答案是”因为 know 是状态动词”。这个答案只是把问题换了个名字,等于什么都没回答:你问”为什么 know 不能进进行体”,它答”因为 know 是状态动词”;你再问”那为什么状态动词不能进进行体”——卡住了。

真正的答案在上一节的摄像机里:推近这个动作,只在画面里有东西可看的时候才有意义。 know 的内部是同质的(homogeneous)——你在”我知道答案”这条线上任取一点切进去,看到的画面跟在任何别处看到的一模一样,就是”知道”。没有阶段,没有进展,没有从 A 到 B 的移动。摄像机推近推了个寂寞。

类比(这个你一定秒懂):

const answerKnown = true   // 编译期常量,运行期不存在"变化"这回事

对一个处处相等的东西做 diff,diff 恒为空。进行体本质上就是一个”时间维度上的 diff 视图”,它要展示的是”此刻与前一刻不同”。

第二问的答案:b 里的 be 已经不是那个”是”了。

  • He is rude. —— be 是连系动词,rude 是他的属性。编译期常量,不可切换,跟身高一样。
  • He is being rude. —— being 把 be 改写成了一次运行期的行为赋值:暂时、可切换、是他”做”出来的,他下一秒就能收起来。

所以 is being 是英语提供的一个”把常量临时降级成变量”的语法旋钮。(至于什么时候能拧这个旋钮,3.2 有一把一个准的检测器。)

回扣卡点 7。 你卡在第二问,是因为你以为规则写在动词上——“这几个词不许加 -ing”。它不在动词上,它在这个义项的形状上。同一个 be、同一个 have、同一个 think、同一个 taste,换一个义项就换一副形状,一副能进进行体一副不能。3.4 会把这件事推到底。

你肯定背过这条规则:“状态动词不用进行时”。

*I am knowing the answer.
*She is owning a car.
*This room is containing three chairs.

背下来了。但为什么?

而且——你要是认真想过,会发现这条规则有个说得不通的地方:我们上一节刚说,进行体的功能是”把点拉成线”。那 know / own / love 这些词,本来就是无限延伸的线啊,它们甚至比 run 更像线(run 还能停下来,know 一知道就是持续状态)。既然它们已经是线了,进行体为什么反而不要它们?

这个反问很关键,如果你能自己撞到它,说明你在真的思考。我们来拆。

3.1 因为进行体要的不只是”线”,它要”有变化的线”

回到摄像机。推近这个动作,只在画面里有东西可看的时候才有意义。

状态动词的问题是:它的内部是同质的(homogeneous)。你在 I know the answer 这条线上任取一点切进去,看到的画面跟在其他任何一点看到的一模一样——“知道”。没有阶段,没有进展,没有从 A 到 B 的移动。

所以摄像机推近推了个寂寞。

Go 类比,这个我特别喜欢:

const answerKnown = true    // 编译期常量,运行期没有"变化"这回事

对一个处处相等的东西做 diff,diff 结果恒为空。进行体本质上是个”时间维度上的 diff 视图”——它要展示的是”此刻与前一刻不同”。

换个说法:进行体要求事件有内部结构(internal phases)。一个事件能进进行体,当且仅当你能把它切成”前半截”和”后半截”,且这两截不一样。climb to 34% 能切(离那条线越来越近),know the answer 不能切(知道就是知道)。

摄影类比同样成立:对一张纯色图做数码变焦,放大到 400% 还是一片纯色,信息增益为零。进行体是变焦,状态是纯色。

所以完整版规则是这样的:

进行体 = 把有内部结构的动态事件拉成线。 点动词进不去(内部为空),状态动词也进不去(内部无差异)。 点动词可以靠”复制到一串”或”改指前段过程”来救;状态动词只能靠——临时性来救。

3.2 救活它:be being + 形容词

现在来看那个真正精彩的例外。

a. He is rude.
b. He is being rude.

中文怎么翻?

  • a. 他很粗鲁。
  • b. 他这会儿故意表现得粗鲁。/ 他这是在撒野。

中文靠加”这会儿""故意""这是”来补。英文靠 is being。

差别在哪?a 说的是本性,b 说的是此刻的可切换行为。

  • He is rude. —— 这是他的属性。不可切换,就像身高。
  • He is being rude. —— 这是他此刻选择的表现。可切换,他下一秒就能收起来。

所以 is being 隐含了三件事:

  1. 暂时性:这是有始有终的一段,不是常态
  2. 可切换:它随时能变回去
  3. 施动性(agentivity):这是他”做”出来的,不是他”是”出来的

Go 类比,这个准到有点过分的地步:

const rude = true      // a. He is rude.        —— 编译期常量,你改不了,他就这样
var   rude = true      // b. He is being rude.  —— 运行期变量,是当下赋的值,随时能改

is being 就是英语提供的一个”把常量临时降级成变量”的语法旋钮。它把一个静态属性改写成一次运行期的赋值动作。

一个好用得离谱的检测器

既然 is being 要求”可切换、可选择的行为”,那形容词就必须描述可控行为。不然就崩:

a. He is being rude / silly / stupid / helpful / careful / patient.   (都行:可切换的行为)
b. *He is being tall / young / intelligent / French / left-handed.   (全崩:不可控的属性)

b 为什么崩?因为你不能”选择”做一个高个子。这一测就灵——你能不能”装”出来?能,就能用 is being;不能,就不能。

(少数例外是修辞性用法,比如夸人 You're being so smart about this.——把本该是属性的 smart 硬说成”你此刻的决策很聪明”,这是有意的修辞迁移,恰恰反证了规则的成立。)

3.3 母案例里的一刀

母案例里有一处特别合适用这个句式:11 月 12 日凌晨 3:05 之后,客户成功经理 Marcus Bell 开始给 Northwind Retail 写通知邮件。那是 Pallet 最大的客户之一,两百多家门店,八万美元的 GMV 就这么没了。

a. Marcus was careful with the wording.
b. Marcus was being careful with the wording.

中文:

  • a. Marcus 措辞很谨慎。
  • b. Marcus 这会儿措辞格外小心。

差别在哪?

  • a 是在评价他的职业素养:Marcus 是个谨慎的人,他写东西一向如此。这是他的属性,跟这次事故无关,跟那天凌晨也无关。它是可迁移的——你下次跟他打交道,他还是这样。
  • b 是在描述他当时给自己安排的动作:凌晨三点半,屏幕上摊着事故时间线,他一个字一个字地掂量”degraded performance”和”outage”之间选哪个、“approximately $80,000”这个数字要不要写出来。这份小心是临时的、是他自己选的、也是可以脱下来的——等他睡一觉起来重读这封邮件,他可能又把它改回去了。

这一刀下去,同一件事的心理距离完全变了。a 把 Marcus 往”专业”那边推,b 把他往”他知道自己在走钢丝”那边推。中文要表达这个差别,得加上”这会儿""格外""一个字一个字地”——英文只需要 was being 两个词。

再补一句同类型的,这次是 Priya:

a. Priya was blunt in the postmortem.        (她这人说话直——性格)
b. Priya was being blunt in the postmortem.  (那天的复盘会上,她刻意把话说得很直——策略选择)

b 里藏着一个判断:她不是不会委婉,她是那天选择了不委婉。blameless 复盘会要让所有人听清楚”流程缺了什么”,所以她把客套收起来了。这个”选择”的信息,中文得加”刻意""那天”才补得出来。

3.4 同一个动词,两个义项,两种形状

再往前推一层,你会发现一件更根本的事:决定能不能进进行体的,不是”哪个动词”,而是”哪个义项”。

拿 have 看:

a. *We are having a car.            (have = 拥有:状态,崩)
b. We are having lunch.             (have = 进行某项活动:事件,完全正常)
c. We are having a great time.      (have = 经历:事件,完全正常)
d. *She is having blue eyes.        (崩)

同一个 have,a 崩 b 顺。所以规则压根不在动词上,在**这个义项的”形状”**上。

Go 类比(这个你天天写):

// 同一个名字,两个不同的类型签名,只有实现了 Process 接口的那个能接 progressive
type Ownership struct { ... }   // 静态数据,没有 Process 方法
type Activity   struct { ... }  // 有 Process() 方法
 
var progressive interface{ Process() } = Activity{}   // OK
// var progressive interface{ Process() } = Ownership{}  // 编译不过

进行体像一个接口约束:be doing 要求它的补语实现”可变 + 有内部结构 + 有终点或临时性”这套契约。own 这个义项不满足,have lunch 这个义项满足。

母案例里就有一个漂亮的双义项,你大概天天写:

a. The orders table has 420 million rows.    (have = 持有/含有:状态,不能进进行体)
b. *The orders table is having 420 million rows.  (崩)

而换成 hold 的”承受”义项,或者换成动态的 grow,就顺了:

The orders table was growing by about 200,000 rows a day.    (动态事件:完全可以)

同理一大堆:

a. I think you're wrong.        (think = 持有观点:状态)
b. I'm thinking about it.       (think = 运转思维:事件,完全正常)

a. The soup tastes salty.       (taste = 尝起来是:状态属性)
b. The cook is tasting the soup.(taste = 尝一尝:动作事件,正常)

a. I feel tired.                (我的状态)
b. I'm feeling tired today.     (今天这会儿,暂时的,可能睡一觉就好)

a. She looks pale.              (她脸色白)
b. She's looking pale today.    (她今天看着有点白——暂时的,可能病了)

b 组全都是临时性在起作用。I'm feeling tired today 里的 today 不是可有可无的装饰——它是”这条线有端点”的证据。没有它,I'm feeling tired 也能说,但临时性就弱一些。

顺带一提:I'm loving it.(某快餐广告语)为什么能成立?因为它是故意把”品牌偏好”这个本该是状态的心理,包装成”此刻正在发生的体验”。广告要的就是这个——让一个持久的东西看起来像是你正在享受的过程。这是营销语法,不是日常语法,但它恰好又一次证明:进行体的核心语义就是”暂时 + 可切换 + 正在经历”。

3.5 一个社交案例:你八成误会过某个人

上面那组 is being 的差别,放在人际关系里是最容易出事的——因为它的代价不是语法分,是一段关系。

场景:上周五团建,你在茶水间跟老张打招呼,他”嗯”了一声就低头看手机。你心里咯噔一下。

a. Zhang was cold to me on Friday.
b. Zhang was being cold to me on Friday.

中文:

  • a. 老张那天对我挺冷淡的。
  • b. 老张那天是故意冷淡我的。

a 是在给老张定性:这人冷淡。一句话出口,它就变成了他对所有人的属性,而且是可迁移的——下次他跟别人也这样,你会在心里说”看吧,我就说”。

b 只描述周五那一次的行为:他在那个具体的场合、那个具体的时刻,选择了不搭理你。可以收回,可以有原因,也可以跟他是个什么样的人毫无关系。

事后你才知道,他妈那天早上进了急诊,他在等检查结果。

那么问题来了:a 和 b,哪一句让这个误会更容易解开?

是 b。因为 b 从形态上就把这件事框成了”一次可撤销的行为”,而不是”一个人的本性”。你没法跟一个属性和解,你只能跟一次行为和解。

这就是为什么英语母语者在争执和调解里特别爱用 was being:它把”你这个人有问题”降级成”你刚才那个动作有问题”,给和解留了一扇门。中文要完成同样的降级,得加上”那天""是……的""故意”好几个词,还常常说不清。

把检测器再跑一遍,这次是同一场团建:

a. She was being distant all evening.       (行:疏远是可控行为)
b. She was being short with me.             (行:说话带刺,可控,能收)
c. *She was being an only child.            (崩:独生子女是身份,装不出来)
d. *He was being taller than me.            (崩:身高不可选)

c d 崩在同一个地方:你没法”选择”做一个独生子女,也没法”选择”比别人高。问一句”这个能装出来吗”,答案立刻就有了。

本节落点: 进行体不只是一个”拉长”的开关,它还悄悄塞进了两个隐藏语义——暂时性和未完成。这两个语义在状态动词那组对比里看得最清楚:He is rude vs He is being rude,差的就是”永久 vs 暂时”。


四、母案例三连:同一件事,三种体,三种心理画面

好了,装备齐了。现在回到 The Outage,把钻石转一下。

事件核不变——2024 年 11 月 12 日太平洋时间凌晨 2:14,Pallet 的后端工程师 Lin Wei 执行一条数据库迁移,锁住了 orders 表(约 4.2 亿行),导致 51 分钟的服务降级,约 12,300 笔下单失败,损失约 8 万美元 GMV,但没有数据丢失。

同一件事,我们给它三种体。

4.1 第一句:一个点

The migration locked the orders table.

中文:迁移锁住了订单表。

这是事故报告标题的写法。

摄像机在外面,全景,咔嚓,事件被压成一个不可分割的点。你得到的信息是:这件事发生了。句号。

它不给你任何画面。你看不见 2:17 那第一条 p99 latency > 2s 的告警,看不见 Lin 的手机在床头静音震动,看不见 2:31 他盯着 SHOW PROCESSLIST 发愣,看不见 2:37 错误率那条线冲上 34%。你只有一个布尔值:true。

它的语用功能是什么?结案。 它适合出现在:事故报告的第一行、状态页(status page)的公告、给客户的事件摘要、季度可靠性报告的表格行。它说话的语气是”我已经把这件事放进档案了”。

注意一个细节:lock 在这里是成就动词(achievement)——瞬时、有界、不可逆。所以它天然喜欢被封装成点。你甚至可以在后面直接钉一个时间戳而毫不别扭:The migration locked the orders table at 2:14. 有界事件和具体时间点,天生一对。

再注意一个细节:这一句没有主语人物。没有 Lin,没有 Priya。主语是 the migration——一个东西,不是一个人。这个选择不是偶然的,它是 blameless 复盘文化的语言体现:把”谁干的”换成”什么导致的”。这件事在第三面(语态与主语选择)会展开讲,这里先留个扣子。

4.2 第二句:一条线,而且被撞断

The migration was still running when Priya joined.

中文:Priya 上线时迁移还在跑。

中文里那个”还”字,就是全部线索。而在英文里,这个”还”不在任何词上——它在 was running 这个形态里,而且你没法省。

这一句的机位在哪?在事件的中间。

你看见了画面:凌晨 2:44,告警按策略自动升级,Priya 的手机响了,她摸黑打开笔记本,登进 Grafana,然后她看见——那条 2:14 发起的 ALTER 还挂在 SHOW PROCESSLIST 里,state 停在 copying to tmp table,进度条(如果有的话)一动不动。错误率 34%,Northwind 的下单请求正在成片失败。她加入了事故频道。

关键的机制是 when 子句和进行体的配合:

a. The migration was still running when Priya joined.    (撞进中间:顺)
b. *The migration locked the orders table when Priya joined.  (撞不进去:别扭)
c. The migration locked the orders table, but Priya killed the connection at 2:47.  (前后相接:顺)

b 别扭在哪?locked 是封装好的点,外壳是硬的,Priya 撞不破。when 要求的语义是”在某件事展开的过程中,另一件事发生了”,而封装好的点没有”过程中”。

c 就顺了,因为 but 只要求先后顺序,不要求”撞进中间”。

这是进行体最基础、也最不可替代的功能:给另一件事提供一块”可以被撞进来的背景板”。

Go 类比:was running 是一个正在运行、尚未返回的 goroutine。另一个 goroutine(Priya joined)进来了,把它打断了。而 locked 是一个已经返回的函数,你没法打断一个已经返回的东西。

一句话记住:要进行”插话”叙事(A 正在做 X,这时 B 发生了),英文强制你用进行体。中文只需要一个可省可不省的”正”或”还”。

顺便注意 still 这个词。它不是必需的——The migration was running when Priya joined. 也完全成立——但它把”这条线比预期长”这层意思挑明了。英文里 still 是可选的强调剂,而 was running 是强制的形态。中文里”还”承担了两者,所以你很难意识到它们其实是两层东西:一层是几何(线),一层是态度(比预期长)。 英文把它们分开了。

4.3 第三句:线 + 尾巴,让人屏息的写法

现在来最狠的一句。

Lin had been trying to kill it for ten minutes.

中文:他试着中止它,试了十分钟了。 (或者:到那时候为止,他已经跟它搏斗了十分钟。)

摄像机在哪?同时在两个地方。

  • 进行体那一半:你在过程内部。你看到的是一条线,它在延伸。
  • 完成体那一半:你站在 Priya 接手的那个点上,往回看。你看到这条线从十分钟前就开始画了,一直画到你脚下,还没停。

两者叠加出来的效果是:

  1. 不是没发现。 这条线有前置长度。他 2:31 就定位到是迁移了——问题从来不是”不知道出事了”。
  2. 发现了,但停不下来。 这才是这句真正让人屏息的地方。runbook 里没有”如何 kill 一个正在跑的 ALTER”这一节。他不知道怎么安全中止——中止一个半途的 DDL 会不会导致回滚、回滚要多久、会不会写坏数据,这些问题在凌晨 2:35 的脑子里全是问号。
  3. 到那个时间点为止,一直没停。 for ten minutes + had been 承诺的是连续性。不是”他试了一次,失败了,然后去倒了杯水”,是”他一直在试”。
  4. 煎熬。 这是心理层面的推论——读者自己算出来的,不是句子说出来的。

这就是这一节标题说的”屏息”。句子本身一个情绪词都没有,没有一个 frustrated、没有一个 desperate、没有一个 helpless。整句的中性程度极高,压迫感全部来自动词形态。

而且你注意,这句话还顺手完成了另一样东西:它为”不处罚 Lin”提供了语言学层面的支撑。 复盘会的结论是流程缺失,不是个人失误;而 had been trying 这个形态本身就在说——他尽力了,是系统没给他工具。你要是写成 Lin failed to kill the migration.,道德重量立刻就变了。同一件事,一个体是免责的,一个体是归责的。

我们做个对照实验,把体换掉,压迫感就没了:

a. Lin had been trying to kill it for ten minutes.      (挣扎、连续、未停)
b. Lin had tried to kill it several times.              (试过几次,之间是断开的)
c. Lin tried to kill it.                                (一次,点,已成定局)

b 换成了完成体的经历用法(详见下一节),several times 把事件复数化了——但”几次”之间是有间隔的,你脑补出的画面是”他试了几下,中间还干了点别的”。这跟”十分钟里一直在”的心理画面差了十万八千里。

c 更弱,就是一次事实记录。

这三句,中文都可以译成”迁移出问题了”,顶多加个”一直”。英文给读者的是三种完全不同的心理画面:归档、监控、挣扎。

4.4 三连总表

句子体摄像机时间形状心理画面适合出现在
The migration locked the orders table.一般外面,压成点点结案、归档事故报告标题、状态页公告
The migration was still running when Priya joined.进行内部,被撞断线监控回放时间线叙述、复盘会口述、纪录片旁白
Lin had been trying to kill it for ten minutes.完成进行内部 + 回看线 + 尾巴挣扎、煎熬复盘文档、调查式报道、事故回顾

这就是”体的力量”: 你没换任何一个实词(除了把 lock 换成 run 再换成 try,那是语义需要),你只换了动词的形状,读者脑中的世界就变了。

中文要靠加”正""还""一直""此前十分钟”这些词来达到类似效果,而且加了也不强制——中文读者读到”迁移锁住了订单表”时,脑子里到底是”点”还是”线”,全靠上下文猜。英文不让你猜,它焊死了。


五、完成体的四种用法:同一个机位的四种看法

传统语法书会把”现在完成时的四种用法”列成四条互不相干的规则,让你背。这是最坑的一种讲法,因为你会以为这是四个东西。

不是。它们是同一个机位的四种看法。

机位永远不变:站在现在,往回看。变的只是你往回看的时候,关心的是什么:

用法你关心的是类比(Go / 系统)
结果现在的状态是怎么造成的查询当前字段值,附带说明它来自哪次变更
经历我的经验库里有没有这条检查一个 append-only 的日志里有没有匹配项
持续这条状态有多长了读取一个计时器 / time.Since(start)
刚刚这条变更的新鲜度读取 lastModifiedAt,判断是不是刚刚

四个查询打在同一个状态上。下面用母案例全演一遍。(时间区间的封闭/开放判据详见第一面,这里默认 2026 年 9 月的”现在”是观察点。)

5.1 结果用法:现在还有痕迹

The incident has been resolved. No data was lost.

中文:事故已经解决了。没有数据丢失。

思维说明:这句话的重点不是”解决”这个动作,而是现在的状态——事故单关了,服务是好的,数据一条没少。完成体在这里回答的问题是:“现在这个局面是怎么来的?“答:“因为发生过一次解决。”

注意后半句为什么是 was lost 而不是 has been lost:No data was lost 是在陈述那 51 分钟里发生的事实(一个封闭的过去区间里的否定事实),而 The incident has been resolved 是在说现在的状态。英文在同一句话里换形态,是因为两半句的机位本来就不同——中文一个”了”字全包了。

再来一个:

Northwind has renewed its contract.

中文:Northwind 续约了。

思维说明:重点不是”他们签过字”,是”现在他们还是客户”。你要是写成 Northwind renewed its contract.(一般过去),那只是在时间轴上钉一个事件,跟”现在”断开——听者会等着你说下文:“然后呢?他们后来还在吗?”

结果的判据很简单:你能不能把它改写成”所以现在……”。 能,就是结果用法。

  • The incident has been resolved. —— 所以现在没有未关闭的 P1 了。成立。
  • Northwind has renewed. —— 所以现在合作关系还在。成立。

5.2 经历用法:经验库里有没有这一条

Lin has run hundreds of migrations since then.

中文:从那以后,他跑过几百次迁移。

思维说明:这句话不关心”现在 Lin 怎么样”,它关心的是他的经验清单——这张清单上有没有”跑过几百次迁移”这一条。有。所以他对迁移的判断是可信的。

而且这句话藏着一个特别漂亮的事实:2026 年的现在,Lin 是 Pallet 的 on-call 培训负责人。 一个出过 P1 事故的人,成了教别人 on-call 的人。这句完成体就是这个反转的技术表达——它不问”他当年犯了什么错”,它问”他脑子里现在有多少条记录”。

经历用法的前提是区间必须开放(这个判据详见第一面):这个人的”人生经验库”还在累积,还没封档。所以:

a. Lin has run hundreds of migrations.   (他还在这行、还在干:经验库开放)
b. Lin ran hundreds of migrations.       (他已转行/已离开这个岗位:经验库封档,只能当历史讲)

b 不是错的,但它把 Lin 放进了”已经归档的人”那一栏。这就是为什么悼词和传记里几乎不用现在完成时描述逝者——死人的经验库关闭了。(同理:*Einstein has visited Princeton. 别扭,因为爱因斯坦的经验库封档了。)

再看一句母案例的:

Priya has handled worse incidents than this in her eight years at Pallet.

中文:Priya 在 Pallet 这八年,处理过比这更严重的事故。

思维说明:这句话的语用功能是给判断背书。它说的是”经验库里有比这更狠的记录”,所以她在 2:47 那一分钟里做决定时手不抖。eight years 这个区间是开放的(她还在,现在是工程总监),所以完成体站得住。

5.3 持续用法:这条状态有多长了

卡点 8(二选一型)。

上个月你在地铁上偶遇一个失联十年的大学同学,站着聊了两站,各自下车。晚上你想发条朋友圈:“我们认识十年了。”

a. We have known each other for ten years.
b. We knew each other for ten years.

选哪句?先别往下看。

打脸时刻。 你选 a,对——b 是一般过去时 + 一段封闭时长,它会把你这个老同学放进”已经归档的人”那一栏,听起来像你们已经断交了,或者其中一个人不在了。

但真正的卡点在下一句。 你想接着写”不过中间有八年完全没联系”。英文怎么办?

*We have known each other for ten years, but we lost touch for eight of them.

这句崩了,而且崩得非常隐蔽:语法全对,形态全对,读起来甚至挺顺。

它崩在哪?have known ... for ten years 是一个持续用法,它承诺这条线连续十年没断过;后半句 we lost touch for eight of them 说这条线断了八年。前一半承诺连续,后一半宣布断裂,两个承诺在同一句话里打架。

怎么救?只能拆:

a. We first met ten years ago, lost touch for eight years, and ran into each other on the subway last month.
   (把"线"拆成三个点:初见、失联、重逢。全部一般过去时。)
b. We have known each other since 2016, on and off.
   (承认它不是一条干净的线,`on and off` 把"断断续续"显式标出来,形态上不再撒谎。)

b 那句里的 on and off 是关键:它不是修饰,它是给持续用法打的补丁——没有它,since 2016 就是在说假话。

回扣卡点 8。 你刚才要是觉得”加个括号说明一下就行了”,那正是中文给你的错觉。中文允许括弧式的补救——“我们认识十年了(不过中间断了八年)“,一句话里先立再补,读者自动合并。英文的完成体持续用法不给你这个位置:它是一句不可分割的承诺,起点对、中间没断、现在还成立,三条里断一条,整句就废。你要么换形态,要么显式标注。

(母案例里那个 *Lin has been the on-call training lead since the outage. 就是这条规则撞在事实上的样子,下面马上讲。)

Pallet has gone 22 months without a P1 incident.

中文:Pallet 已经有 22 个月没出过 P1 事故了。

思维说明:这句的重点是长度——一条从 22 个月前开始、一直铺到现在的线。它承诺三件事:22 个月前开始(也就是那三条行动项落地之后)、中间没断、现在还在保持。

持续用法几乎总带着 for / since,而且要求动词短语是可延续的。go without a P1 incident 是可延续的状态,所以能持续。

这里有个必须做的检查:这个状态现在还成立吗?

持续用法有一个隐藏承诺,很多人写的时候从来不检查,然后句子就悄悄变成了假话。承诺是:这个状态从起点到现在,一直没断过,而且现在还成立。

母案例给我们一个现成的、撞得粉碎的例子:

a. *The orders table has been locked since 2:14.
b. The orders table was locked for thirty-three minutes.

a 语法完美,形态完美,since 2:14 也给得规规矩矩,但它是假的。锁在 2:47 就被 Priya KILL 掉了,这个状态早就结束了。完成体的机位站在 2026 年的现在,往回一看——那条线在 33 分钟处断得干干净净,根本接不到现在。

要救它,只能把机位挪回过去:b 用一般过去时 + for,把那段锁表封进一个封闭的过去区间里。它不再承诺”现在还锁着”,只承诺”那段历史的长度是 33 分钟”。

再来一个不那么极端的错误,它更隐蔽:

*Lin has been the on-call training lead since the outage.

这句的毛病不是”状态结束了”,而是起点选错了。since 承诺的是”从那一刻起就是这个状态”,可 Lin 不是 2024 年 11 月事故当天就当上培训负责人的——他是后来才接的。你要么把起点改对(since last spring),要么改成一般过去 + 一段模糊时间(He took over on-call training a while back)。

这个坑有意思在哪?在于体本身没有撒谎,撒谎的是语义。但它揭示了一条更深的规则:

持续用法是一句不可分割的承诺:起点正确 + 中间没断 + 现在还成立。 三条里断一条,整句就废了。英文的形态逼着你做事实一致性检查——你没法像中文那样说”他一直是培训负责人(不过中间换过一阵)“,那种括弧式的补救,英文的形态不给你留位置。

中文的”一直”可以松,英文的 has been ... for / since 松不了。

所以以后你每次写完成体的持续用法,在心里跑一遍这三问:起点真的是那个时候吗?中间断过吗?现在还是这样吗? 三问全过,再落笔。这一条在你写英文事故报告、写周报、写简历的时候,价值极高——因为这三类文本里全是持续用法,也全是最容易被挑出事实漏洞的地方。

一个学习场景:背了三个月单词,还是开不了口

持续用法还有一个更日常、也更扎心的版本。

场景:你今年六一开始背单词,每天四十个,一天没断,到现在九月初。昨晚跟外国同事对需求,开口还是卡壳,最后憋出一句 “Let me share my screen”。

a. I have learned three thousand words.
b. I have been learning English for three months.

a 用的是完成体的结果 / 经历用法,语法没问题,但它会被误读:听者默认你在宣布一个结果——“这三千词我已经拿下了”,于是他会立刻用正常语速跟你聊下去,然后你就露馅了。你真想说的其实不是”学会了”,而是”过了一遍”:

I have gone through three thousand flashcards.   (卡片过了一遍:不代表会用)

b 才是你昨晚真正想说的那句:一条从六月铺到九月的线,还在铺,而且这个过程本身在消耗你。have been learning 天然带一层”我投入了,但离终点还远”的意思——这恰好就是卡壳那一刻的真实感受。

把体换掉,看看会跑偏到哪去:

c. I learned English for three months.   (封档:听起来像你放弃了,或者那段经历已经结束)
d. I am learning English.                (只有此刻:丢掉了"三个月"这份沉没成本)

c 是中文母语者最容易脱口而出的错误。你想说”我学了三个月英语”,直译就是 I learned English for three months;但在听者耳朵里,一般过去时 + 一段封闭时长 = “我学过三个月(然后就没继续了)“。你要是还在学,就必须用 b。

一句判据收口:想说”到现在为止还在做”,用完成进行;想说”做过,已经结束”,用一般过去 + for。 中文一个”了”字抹平的差别,英文分得清清楚楚——而且分错的时候,对方会以为你放弃了。

5.4 刚刚用法:新鲜度

Marcus has just finished the customer notice.

中文:Marcus 刚写完客户通知。

思维说明:just 把镜头往回推的距离压到最短——几乎是贴着现在。它的语用效果是”这条变更还是热的”。凌晨 3:05 服务恢复,他开始写,写完按下发送的那一刻——热乎的。

这一类几乎总是跟 just / already / yet / recently / so far 一起出现:

a. Marcus has just finished the customer notice.            (刚刚:变更还是热的)
b. Marcus has already sent the Northwind update.            (已经:比预期早,重点在"你不用再催了")
c. Marcus hasn't heard back from Northwind yet.             (还没:预期的回应尚未发生,但窗口还开着)
d. Lin has already written the runbook section on aborting a migration.  (已经:文档里有了)

d 那句是三条行动项里的一条——runbook 补”如何中止迁移”。你注意它的机位:站在现在,往回看,宣布”这一节存在了”。它不告诉你他哪天写的、写了多久,那些是一般过去时的活儿。

already 和 yet 携带的不是时间信息,是说话人的预期:already = “比你想的早”,yet = “我预期它该发生了但它还没”。这两个词是第七章(确定性与言据)的常客,这里先认个脸。

5.5 四种用法的统一视角

你可能会问:我怎么知道别人说的是哪一种?

答案是:你不需要”选一种”,四者是叠加的。 同一句 has done,在不同上下文里被读作不同用法,而所有四种用法共享同一个底层语义:

完成体 = 站在参照时刻,宣布”这个事件在参照时刻之前已经发生,并且它与参照时刻相关”。

差别只在”相关”体现在哪:状态相关(结果)、清单相关(经历)、长度相关(持续)、距离相关(刚刚)。

这就是为什么完成体在英文里用得那么频繁——它不是一个”时态”,它是一个把过去和现在焊起来的接口。中文没有这个接口,中文靠”了""过""已经”这些零散的词加读者脑补。

母案例里最典型的例子就是那句 22 个月。你想想,这句话在中文里怎么说?“Pallet 已经 22 个月没出过 P1 事故了。“一个”了”字。可英文要同时交代:起点在 22 个月前(跟那次事故挂钩)、中间没断、现在还成立、这是一件值得说的事——四个信息,一个 has gone 全扛了。


六、完成进行体的”额外信息量”

现在来啃最后一块硬骨头。

很多人以为”完成进行时 = 现在/过去完成时 + 强调持续”。这个说法不算错,但严重低估了它。完成进行体真正特殊的地方,是它塞进了两条纯粹额外的信息,这两条信息你用任何其他形态都拿不到。

6.1 经典最小对立对

卡点 9(二选一型)。

先别管规则。下面两句话,前面都能配上同一句”她眼睛红了”:

a. Her eyes were red. She had been crying.
b. Her eyes were red. She had cried.

问题不是”哪句对”——两句都对。问题是:哪一句让你更想抱抱她?为什么?

先别往下看,想三秒。 这个”想抱抱她”不是在煽情,它是一个非常精确的语言学探针:它在问你,这两句给出的画面浓度是不是一样的。

打脸时刻。 八成你选 a,理由是”a 多了一个’一直’的意思”。这个答案只答对了 10%,而且恰恰是把最关键的部分绕过去了——你答的是翻译(had been crying 被你译成了”一直在哭”),不是机制。所以我要追问一句:把中文里的”一直”拿掉,你还能说出区别吗?

答不上来就对了。往下看,先把中文对照和完整的机制读完,读完我再回扣你刚才为什么会卡在这一步。

a. Her eyes were red. She had been crying.
b. Her eyes were red. She had cried.

中文,几乎一样:

  • a. 她眼睛红了。她一直在哭。
  • b. 她眼睛红了。她哭过。

你现在能不能说出它们的差别?先自己想三秒。

差别是这样的:

  • a 你看见的是过程 + 残留。 她刚哭完。眼睛红是哭这个过程的直接痕迹,头发可能还乱着,纸巾还在手里,你甚至能感觉到她呼吸还没平下来。摄像机刚刚从”哭泣”这个事件内部退出来,画面还带着体温。
  • b 你听到的是一个事实陈述。 她哭过。什么时候?不知道。一小时前?昨天?哭了一次还是三次?都不承诺,也不重要。眼睛红可能是另外的原因。

所以:

完成进行体 = 完成体 + “过程可见” + “痕迹还在”。

Go / 运维类比,非常贴:

# had cried        —— 日志里有一条 ERROR,你只知道它发生过
$ grep -c ERROR app.log
3
 
# had been crying  —— profiler 刚停下来,火焰图上还热着,堆栈还在内存里
$ go tool pprof -http=:8080 heap.out

grep 给你计数(事件发生过 N 次),pprof 给你过程(哪些函数在跑、跑了多久、占用多少)。完成体是 grep,完成进行体是 pprof。

回扣卡点 9。 你刚才之所以”更想抱抱 a”,不是因为 a 更长、更有文采,而是因为完成进行体多给了两件完成体给不了的东西:一是过程可见(你能看见她哭的那段),二是痕迹还在(眼睛红不是巧合,是刚留下的)。而你刚才答不上来,是因为你的中文直觉把这两条全都外包给了翻译——你以为自己在比较意思,其实你在比较两个中文译本。英文里根本不存在”一直”这个词,那份”一直”是 had been 这个形态自己长出来的。这两条加起来,才产生了那个你想抱她的冲动。

再往回扣一层:这说明情绪是可以由形态单独承载的。整句里没有一个形容词,没有 sad、没有 upset、没有 heartbroken,压迫感百分之百来自动词形状。这也是本章一直在说的那件事——中文得加”一直""刚刚""还”才补得出这层意思,英文的 had been crying 四个音节全给了。

6.2 母案例版

把同样的机制搬到 The Outage。场景:凌晨 2:44,Priya 上线,接手。

a. Lin's hands were shaking. He had been trying to kill the migration for ten minutes.
b. Lin's hands were shaking. He had tried to kill the migration.

中文:

  • a. 他的手在抖。他试着中止这条迁移,试了十分钟了。
  • b. 他的手在抖。他试过中止这条迁移。

a 你看到的是:他试了很久,试了很多次,SHOW PROCESSLIST 刷了一遍又一遍,KILL 敲下去又怀疑自己是不是在制造第二个事故,手抖是这十分钟持续消耗留下的生理残留。这句话带着时长和强度,还带着一层没写出来的东西——他不是在偷懒,他是在硬扛。

b 你看到的是:他试过。这是个事实,可能只试了一次,可能试了三秒。至于手为什么抖——句子没说,你自己猜去。而且 b 的潜台词有点冷:它像在列举他的行为清单,为的是后面接一句”但他没成功”。

a 里那个”一直”和”硬扛”,中文得靠加字才能出来。英文只用 had been trying 四个音节就全给了。

这一句的实战价值在哪?在复盘文档里。你要写一份 blameless postmortem,你的目标是让读者理解”系统没有给人留下正确的出路”,而不是”这个人不行”。a 天然服务于前者,b 天然滑向后者。形态是有立场的。

6.3 完成进行体到底多给了什么?

把账算清楚,一共三条:

第一条:过程的可见性。 完成体把事件压成一个点(“发生过”),完成进行体保留线(“一直在发生”)。这条最明显。

第二条:暂时性。 完成进行体从来不描述永久状态。它描述的是一段有始有终的过程。

a. Lin has worked on the Order API since he joined.      (持续用法:他一直在做这块,长期状态)
b. Lin has been building the on-call training program.    (暂时的:这是一个有期限的项目,不是他的永久身份)

b 里的完成进行体天然带”这是过渡性的、做完就换”的味道。搭项目、写文档、临时顶班、过渡期——这些语义跟完成进行体是天生一对。反过来,a 里的 has worked ... since he joined 描述的是一条可能无限延伸的状态,它不暗示结束。

第三条:刚停下,或者还没停。

a. It has been raining.           (刚停:地面还湿着)
b. It has rained.                 (下过雨:可能是上周)

a 的经典语境是你往窗外看,地是湿的,你说 “It has been raining.”——你在从痕迹反推过程。b 则是查气象记录。

母案例版也有一句现成的:

a. Orders have been failing.        (刚停/还在失败:地上还有尸体,你看得见)
b. Orders have failed.              (失败过:这是统计口径里的一条记录)

凌晨 2:50 你在事故频道里说的是 a;11 月 13 日上午复盘会上你引用的是 b。前者是战场报告,后者是战史条目。

远场一:马拉松跑崩(运动)

把完成进行体搬到赛道上,你会看得更清楚——因为跑步是唯一一个”长度本身就是全部意义”的活动。

场景沿用 1.6:2025 年 10 月北马,35 公里处小腿抽筋,最后 7 公里走了将近 4 公里。

a. He had run thirty kilometers when his legs gave out.
b. He had been running for three hours when his legs gave out.
c. He had run three marathons before this one.

中文几乎一样:

  • a. 他跑了三十公里的时候,腿罢工了。
  • b. 他已经跑了三个小时,这时腿罢工了。
  • c. 这之前他跑过三次全马。

a 给你的是里程(有界:三十公里是终止条件,他跑到了);b 给你的是消耗——三个小时这条线,直接怼到抽筋那个点上,你读到的是”他的腿不是突然坏的,是被三个小时磨坏的”。c 是经历用法,说的是经验库里有没有这条,跟这次崩不崩没关系。

真正该品的还是那句 b。它一个形容词都没有,但它把”崩”变成了一个可计算的结果:三小时 + 21 度 + 5:10 的配速,读者自己会把账算出来。而 a 只是把两个事实摆在一起。

再补一句赛后你会发的朋友圈:

I'm fine. My legs have been shaking since kilometer 35.

跟 7.2 那句 I'm fine. I've just been up with her since two. 是同一个模具:have been shaking 已经把”我快散架了”说完了,前面那句 I'm fine 自动变成反讽。形态比词汇诚实,这条规则在两个完全不同的领域里跑了同一遍。

远场二:戒烟反复失败(医疗 / 健康)

再来一个领域,这次是戒烟。场景:你 2022 年第一次戒,撑了四十天;2023 年戒了两次,都不超过两周;2024 年 11 月那次事故之后又重新开始抽;现在是 2026 年 9 月,你在尝试第五次。

a. I have quit smoking three times.                    (完成体 + 次数:三次离散的尝试)
b. *I have been quitting smoking three times.           (崩:三个点画不成一条线)
c. I have been trying to quit for four years.           (完成进行 + 长度:一条断断续续但一直没放下的线)
d. I was smoking again within a week of the outage.     (一般体 + 点:那次事故后一周内就复吸了)

a 和 c 的差别特别值得玩味,因为它们描述的其实是同一段人生:

  • a 把它记成三笔离散的记录。听者会问:“哪次成功了?“——而你知道一次都没有。quit 在这里被当成了有界动词(终点是”戒掉”),所以每”戒”一次就是一笔,三笔之间互不相干。
  • c 把它记成一条拖了四年的线。它不承诺成功,它承诺的是没有放弃。这两个说法的道德重量完全不同:前者听起来像一份失败清单,后者听起来像一个人一直在跟自己较劲。

b 为什么崩,下一小节专门讲——它是本章最实用的一条禁区。

6.4 一个极其实用的禁区:完成进行体不吃”次数”

卡点 10(判断对错型)。

先独立判断一句,别看下面的解释:

*I have been quitting smoking three times.

这句话不是语法错误。时态对,分词对,语序也对,每个词你都认识。那它错在哪?

先别往下看,想三秒。答不上来也算一种答案。

打脸时刻。 八成你会开始怀疑是 three times 的位置不对(“是不是该放前面?”),或者干脆觉得”我反正不会这么说,记住就行”。都是躲。它崩的原因是几何矛盾:

  • 完成进行体承诺的是一条连续的线;
  • “三次”是三个中间断开的点。

你没法用一条连续的线去画三个点。 这不是搭配问题,这是几何问题——就像你不能用一次 for 循环的三个断点拼出一条连续的时间序列。

回扣卡点 10。 你之所以想把它归到”语序”或者”固定搭配”上去,是因为那两个类别是可以背的,而”几何矛盾”是需要你真的看见形状才能判断的。这一章从头到尾都在逼你做同一件事:先看见形状,再谈规则。

要救这句,你得先决定你到底想画什么:

a. I have quit smoking three times.                     (想画三个点:用完成体 + 次数,顺)
b. I have been trying to quit for four years.            (想画一条线:用完成进行 + 长度,顺)
c. I have been trying to quit repeatedly for four years. (想画一条"粗糙的线":加 repeatedly 抹平,也顺)

c 里的 repeatedly 是个关键词:它把离散的点抹成了一条”反复发生”的粗糙线,所以能过。没有它,光有 three times 就过不了。

把同样的几何矛盾放回母案例,你会看得更清楚:这条规则很少有人讲,但它能救你一大片错句。

a. Lin had been trying to kill it for ten minutes.       (线:连续十分钟)
b. Lin had tried to kill it three times.                 (点×3:三次)
c. *Lin had been trying to kill it three times.          (崩)

c 为什么崩?

因为完成进行体承诺的是一条连续的线。而”三次”是三个离散的点——点之间是有间隔的,不连续。你没法用一条连续的线去画三个中间断开的点。这是几何矛盾,不是搭配问题。

所以: 完成进行体配 for / since / all night / the whole time 这些长度标记(连续),不配 three times / twice / several times 这些次数标记(离散)。

反过来讲,如果你想表达”断断续续地做过很多次”,你得用完成体 + 次数,或者用完成进行体 + 一个”反复”的副词:

a. Lin had tried to kill it several times.                       (完成体 + 次数:顺)
b. Lin had been retrying it repeatedly for ten minutes.          (完成进行 + repeatedly + 长度:也顺,但画的是"反复"这条粗糙的线)

b 里 repeatedly 把离散的点抹平成了一条”反复发生”的粗糙线,所以能过。没有它,光有 three times 就过不了。

6.5 四种体的信息量排序

做个总结账,你会发现这四种体排成一条信息量递增的阶梯:

一般体      locked                     1 位:发生过
完成体      had locked                 2 位:在参照点之前发生过 + 与参照点相关
进行体      was running                2 位:那一刻正在展开 + 未承诺终点
完成进行体  had been trying            4 位:过程 + 长度 + 未停/刚停 + 痕迹

信息量最大的形态,也是最难用对的。 母语者不会在每句话上都用 has been doing,因为它的信息量是有代价的——你承诺了长度、连续性和痕迹,就得保证这些都是真的。中文没有这个承诺机制,所以中文使用者写英文时最常见的问题不是”用错完成进行体”,而是”该用的时候没用,画面就平了”。

母案例最能说明这一点。你要写一句”Lin 尝试中止迁移”,最省事的写法是 Lin tried to kill the migration.——语法正确,信息完整,读者不会抗议。但它丢掉了这件事故里最要命的那个部分:他不是没试,他是试了十分钟。 这一层东西,只有 had been trying 给得了。


七、卫星案例:把机位带回家

母案例毕竟是一次极端事件(而且说实话,它离你的日常太近了,容易被职业情绪盖住语言学的东西)。现在把这四副机位拿回日常生活,你会发现它们天天在你身边跑。

7.1 外卖:App 上的状态机

你每天都在看的外卖 App,本质上就是把”体”做成了一个 UI。

a. The rider is delivering your order.        (进行体:DELIVERING,中间态)
b. The rider has delivered your order.        (完成体:DELIVERED,终态 + 结果)
c. The rider delivered your order at 18:40.   (一般体:时间点上的事实记录)

中文:

  • a. 骑手正在配送你的订单。
  • b. 骑手已送达你的订单。
  • c. 骑手 18:40 送达了你的订单。

中文 UI 上写的是”配送中”和”已送达”——两个词。英文 UI 上写的是 is delivering 和 has delivered——两个形态。 你注意到没有,App 的状态机其实就是一台摄像机:PENDING -> DELIVERING -> DELIVERED,中间那个 -ing 状态就是”过程可见、终点不可知”。

再来看一句你一定见过的(或者该说,一定想说但不知道怎么说的):

The rider has been circling your block for ten minutes.

中文:骑手在你那个街区绕了十分钟了。

这句的恐怖之处在哪?完成进行体给了你三条信息:他绕了十分钟(长度)、还在绕或刚停下(未停)、他迷路了(从”绕圈 + 十分钟”推出来的)。你什么评价词都没写,但用户读完就知道自己的饭要凉了。

对比一下:

The rider has circled your block.     (他绕过你的街区。事实陈述,可能绕了一圈就走了)

这句在配送语境里几乎没用。这就是完成进行体的实战价值。

7.2 孩子半夜发烧

这一组离技术最远,但情绪浓度最高——正好用来检验”体 = 情绪”这个说法。(这一节的母案例气质太近,所以我特意挑了一个完全不同的场景。)

a. The fever broke at 4 a.m.                          (一般体:点。凌晨四点退烧了。事实,句号。)
b. I was changing the compress when she threw up.     (进行体:线,被撞断。)
c. I have checked her temperature six times tonight.  (完成体 + 次数:今晚上量过六次了。)
d. I have been checking her temperature all night.    (完成进行:一整夜都在量,刚量完或者还要量。)

中文:

  • a. 凌晨四点烧退了。
  • b. 我正给她换毛巾的时候,她吐了。
  • c. 我今晚给她量了六次体温。
  • d. 我一整夜都在给她量体温。

c 和 d 的中文几乎一样(“量了一晚上”),但在听者耳朵里完全不是一回事:

  • c 听上去像你在汇报工作量,而且听者会立刻追问:“那现在多少度?“——因为完成体把六次压成了六个离散的点,点是不分”进行中”和”已结束”的。
  • d 听上去像你在求救或者在说自己快撑不住了。“一整夜""还在量""我眼睛已经看不清水银柱了”这几层意思,全在 have been checking 里。

b 那句再看一眼:was changing 是一条线,threw up 是一个点——线被点撞断,这是进行体最经典的用法。你要是写成 *I changed the compress when she threw up.,时间关系立刻乱掉:读者会以为你换完毛巾、站起来、然后她吐了。

再来两句你肯定在心里说过但没翻出来过的:

I've been sitting by her bed since midnight.        (一直在旁边坐着,没合眼)
I have sat by her bed since midnight.               (怪:sit 在这里被压成了点,听起来像在报数)

第一句地道,第二句别扭。为什么?因为”守在床边”这种高强度、持续消耗的状态,天然需要完成进行体来表达”过程本身正在消耗我”。完成体把它压成了点,就浪费了这个动词的全部表现力。

最后一句,你可以自己检验一下有没有真的懂:

I'm fine. I've just been up with her since two.

为什么这句好笑?因为 have been up 已经把”我快散架了”说完了,前面加一句 I'm fine 就成了反讽。形态比词汇诚实。

7.3 装修

a. They painted the apartment.                     (一般体:点。他们刷过房子。事实。)
b. They are painting the apartment.                (进行体:线。正在刷,还没完。)
c. They have painted the apartment.                (完成体:结果。刷完了,现在墙是新的。)
d. They have been painting the apartment all week. (完成进行:搞了一整周,油漆味还在。)

中文:

  • a. 他们刷了房子。
  • b. 他们正在刷房子。
  • c. 他们把房子刷好了。
  • d. 他们刷了一整周的房子。

c 和 d 差别最妙:

  • c 的潜台词是”你可以去看房了 / 可以搬进去了”——重点在结果可用。
  • d 的潜台词是”我一整周都没法住这儿,到处是油漆味”——重点在过程的折磨。

同一个事件,一个用来邀功,一个用来诉苦。全靠形态。

7.4 健身

a. He has run ten kilometers.      (完成体 + 有界宾语:跑完十公里了,成绩记录)
b. He has been running.            (完成进行:刚跑完,还在喘,衣服是湿的)
c. He runs every morning.          (一般体 + 习惯:这是他的常态)
d. *He is knowing how to run.      (崩:状态动词,见第三节)

a 和 b 的对比特别干净:a 里 ten kilometers 是终止条件(有界),所以他跑完了,这是个成果。b 里没有终止条件,只有”跑”这条线 + 残留的汗。前者是数据,后者是画面。

如果你想说”他刚跑完步”(浑身是汗地出现在你面前),你是不能说 He has run. 的——那句的意思是”他跑过步(他有这个人生经验)“,听着像在证明他会跑步。你得说 He has been running.

7.5 等一个人

这一组最能体现”体 = 情绪”。

a. I waited for her for an hour.                         (一般体:我等了她一小时。结案,情绪封闭。)
b. I was waiting for her when she called.                (进行体:线被撞断。)
c. I have been waiting for her for an hour.              (完成进行:还在等 / 刚等到,带怨气。)
d. I had been waiting for an hour when she finally showed up.  (过去完成进行:怨气值最高。)

中文:

  • a. 我等了她一小时。
  • b. 我正等她的时候,她打电话来了。
  • c. 我已经等她一小时了。
  • d. 她终于出现的时候,我已经等了一小时了。

a 和 d 的差距有多大?

a 是一个封了口的信封。事情结束,情绪归档,说这句话的人可能已经不在乎了。你甚至可以用它来描述一件十年前的事:1998 年我在火车站等了她一小时。

d 是一个计时器正在你面前跳。它把”一小时”这段长度,直接怼到”她终于出现”这个点上。finally 是可选的——就算没有 finally,had been waiting 本身就已经在抱怨了。

中文要写出这个差别,得靠”已经""终于""都……了”这些词,外加语气。英文只要把 waited 换成 had been waiting,怨气值自动 +50。

最后一句,你可以自己检验一下有没有真的懂:

I'm not angry. I've just been waiting here for forty minutes.

为什么这句好笑?因为 have been waiting 已经把”我很生气”说完了,前面加一句 I'm not angry 就成了反讽。形态比词汇诚实。

7.6 汇报被老板打断(工作)

周三上午十点,季度规划会。你做到第二十一页的 deck,讲到”迁移到 gh-ost 之后的收益”这一页,VP 抬了一下手:“这块先跳过,直接说成本。”

a. I was presenting the Q3 plan when she cut me off.        (进行体:线被点撞断)
b. *I presented the Q3 plan when she cut me off.            (别扭:点撞不进点)
c. I had been preparing that deck for two weeks.            (完成进行:两周,刚被浪费)
d. She has been cutting people off a lot lately.            (完成进行 + lately:暂时状态)

中文:

  • a. 我正在讲 Q3 方案,她打断了我。
  • c. 那份 deck 我准备了两个星期。
  • d. 她最近老打断人。

c 是这组里最狠的一句:你没说”我很失望”,一个字都没说——但 had been preparing ... for two weeks 已经把这两周的重量全压到”她抬手”那一下上了。读者自己会算出这笔账。你要是写成 I prepared the deck for two weeks.,它就变成了一条人事档案里的记录,没有温度。

d 跟 3.5 那条是同一个模具:She cuts people off.(她这人就这样,没救了)vs She has been cutting people off a lot lately.(她最近这样,可能是有压力)。一个定性,一个限时。 后者给关系留了余地,前者没有。

7.7 竞品抢先发布(工作 / 商业)

2026 年 3 月,你们团队做”批量订单导入”做了半年,原定 4 月 8 号上线。3 月 31 号早上,竞品在他们的 changelog 里发了同一个功能。

a. They shipped it first.                                    (一般体:点,事实,残酷而干净)
b. We had been building the same feature for six months when they announced it.  (完成进行:半年 + 被截断)
c. We built the same feature for six months.                 (一般过去 + for:听起来像你们做完了,且已经结案)
d. They have been shipping faster than us all year.          (完成进行:一条持续一年的对比线)

b 和 c 只差一个形态,锅里的内容完全不同:

  • c 听起来像你们完成了,只是没提后续。听者会问:“那你们上线了没?”
  • b 明确地把”半年”这条线怼到”他们官宣”那个点上,句子里那个没写出来的下半句是——然后这半年就白做了。而这一层意思,b 一个字都没写,是 had been building ... when 这个结构算出来的。

a 值得单独说一句:四个音节,一个实词加一个副词,最干净的事实陈述。越是不想多谈的事,越要用一般体。 这就是封装的用途——把半年的沉没成本压成一个布尔值。

7.8 搬家摔碎东西(生活)

2026 年 6 月,你从海淀搬到朝阳,三十公里。你妈留下的那个青花瓷瓶,你亲手包的报纸,放在后备箱的纸箱里。

a. The vase broke.                                     (点:成就动词,不可逆)
b. The vase was broken when we unpacked.               (结果状态:开箱时它已经是碎的了)
c. *The vase was breaking.                             (崩:除非你在放慢动作视频)
d. It had been rattling in the box for the whole drive. (完成进行:一路都在响)

d 是这一组里最有杀伤力的,因为它悄悄改变了责任的归属。

  • a / b 把碎这件事钉在”某个瞬间”上:谁碰的?不知道,可能是路上一个坑。
  • d 把时间轴往前推了三十公里:它不是在搬家那一刻碎的,它一路都在响,你听见了,你没管。

一个形态,把”意外”改写成了”早有预兆”。这就是完成进行体在工程和法务语境里的真实用途——它是追责工具。中文要说清这层,得加”一路上一直在响”七个字;英文靠 had been rattling。

7.9 拔智齿(医疗)

右下那颗智齿,2024 年体检的时候就发现了,片子拍了,医生说”早点拔”。你拖到 2026 年 8 月才去,拔完肿了三天,只能喝粥。

a. The dentist pulled the tooth.                     (点:成就动词,有界,一次完成)
b. My face was swollen for three days.               (结果状态:线,长度三天)
c. I had been putting it off for two years.          (完成进行:拖了两年——没写"我错了",但意思到了)
d. *I was knowing it would hurt.                     (崩:状态动词)
e. I had been dreading it for two years.             (完成进行:怕了两年)

c 同时交代了两件中文得用两句话才说得完的事:早就该做 + 一直没做。你没说”我拖延”,had been putting it off 自带这层评价——put off 这个短语动词本身带贬义,had been + for two years 把这份贬义拉成了一条两年的线。

b 又一次是”动作是点、结果是线”:pull(拔)只需一瞬,be swollen(肿)挂了三天。你不能说 *The dentist pulled the tooth for three days.——那是在说医生拔了三天。中文”拔完肿了三天”清清楚楚,是因为”完”这个字在替你切分;英文没有这个字,它用形态切。

7.10 被朋友放鸽子(社交)

约了周五晚上七点,火锅店。七点四十他发消息:“在路上了,有点堵。” 八点二十:“实在抱歉,今天来不了了。”

a. He stood me up.                                   (点:一次性事件,已成定局)
b. I was waiting at the door when he texted.         (进行体:线被点撞断)
c. I had been waiting for forty minutes.             (完成进行:四十分钟,怨气值拉满)
d. He has been flaky lately.                         (完成进行 + lately:暂时状态,不是定性)
e. He is flaky.                                      (一般体 + 状态形容词:定性,等于判了这段关系)

d 和 e 的对比,是本章”暂时性”这条线在社交场合的直接应用:

  • e 是属性:这人不靠谱。它是可迁移的——下次他迟到,你会说”看吧”。而在英语里,属性是不可切换的,所以 e 实际上是在建议”别再约他了”。
  • d 是暂时状态:他最近这样。lately 给这条线画了端点,端点之外是什么样,句子不承诺——所以你还能替他找理由(是不是工作压力大?是不是家里有事?)。

一个形态的选择,决定了这段友谊还有没有救。 这就是为什么英语母语者抱怨人的时候,特别爱加 lately / these days / this week——那不是时间状语,那是给对方留的台阶。

c 再看一眼:四十分钟这条线,加上火锅店门口的白气和你手机屏幕上的”对方正在输入”,句子里一个”生气”都没写。怨气全是 had been waiting 给的。

7.11 地铁故障(公共)

2026 年 4 月某个工作日早高峰,10 号线,列车在隧道里停了。应急照明亮起来,车厢里先是安静,然后开始有人叹气。

a. The train stopped at 8:12.                        (点:事实记录)
b. Passengers were standing in the dark when the announcement came.  (进行体:线被撞断)
c. Passengers had been standing in the dark for twenty minutes.      (完成进行:二十分钟 + 痕迹)
d. The line has been down since 8:12.                (持续用法:只在 8:30 那会儿说才成立)
e. The line was down for twenty-two minutes.         (一般过去 + for:封档,事后报告用)

d 和 e 是 5.3 那套”三问检查”的实战题。同一件事,两种说法,取决于你什么时间说:

  • 早上 8:30,你还困在隧道里:The line has been down since 8:12. 成立——起点对、中间没断、现在还成立。
  • 当天下午三点,你在办公室跟同事复述:*The line has been down since 8:12. 就变成了假话(线路九点就恢复了)。你得换成 e,把那段封闭进一个过去区间里。

中文里”这条线从八点十二就瘫了”在下午三点说,听者完全不觉得有问题——他会自己在脑子里补”然后后来好了”。英文不让你补:形态的承诺必须是真的,否则就得换形态。 这就是那句老话的工程含义——英文逼你做事实一致性检查。


一面之得

这一面的东西可以压成五条。

第一,体不是”时态的附属品”,它是一台摄像机。 一般体把事件压成点(封装,只给你布尔值),进行体推进内部(拉成线,不承诺终点),完成体退到结果处回看(给你痕迹和连线),完成进行体两个机位同时开(过程 + 长度 + 残留)。你每写一句英文,都在做一次机位调度——而且是强制调度,不能弃权。

第二,动词自己带形状,体是在形状上做手术。 点动词(notice / escalate / lock)内部为空,进行体进不去,除非你把它复制成一串(orders were failing)或者改指它前面的过程(was dying)。有界短语(kill the connection)有终止条件,才谈得上”完成”;无界短语(run)没有,所以它只能给你”经历过”或”持续了多久”。还有一条最容易翻车的:瞬时动作会产生持续结果,动作的长度和结果的长度在英文里是两个东西——The migration locked the table at 2:14. 是点,The table was locked for 33 minutes. 是线,中文一句”锁了三十三分钟”把两者混在一起,英文不许你混。一个免费的检测工具:in five minutes = 跑完花了多久(只有有界的能过),for ten minutes = 持续了多久(而且有界短语一旦配 for,反而暗示没到终点——Lin tried to kill the migration for ten minutes 的意思就是他没成功)。并且这套形状不是技术场景的特例:机房(locked at 2:14 / was locked for 33 minutes)、厨房(burned at 7:49 / was burnt when I got back)、赛道(gave out at 35 km / was shaking for an hour)三个毫不相干的现场,排出来是同一张表。

第三,进行体有两个隐藏语义:暂时性和可切换。 最锋利的证据是 Marcus was careful with the wording(职业素养,编译期常量)vs Marcus was being careful with the wording(那天凌晨那一刻的刻意选择,运行期变量)。状态动词之所以通常不能进进行体,不是因为它们不是线,而是因为它们的内部同质——推近没有信息增益,对纯色图做数码变焦等于白做。

第四,完成体的四种用法(结果、经历、持续、刚刚)是同一个机位的四种看法。 机位永远站在参照点往回看,变的只是你关心”状态""清单""长度”还是”新鲜度”。别背四条,背一条:完成体是一个把过去和现在焊起来的接口。但有一条硬约束你必须养成习惯:写持续用法之前,做一次三问检查——起点真的是那个时候吗?中间断过吗?现在还成立吗? *The orders table has been locked since 2:14. 这句语法挑不出毛病,但它是假的,因为锁在 2:47 就释放了。

第五,完成进行体是信息量最大的形态,也是承诺最多的。 它同时给你过程、长度、未停(或刚停)、残留痕迹。代价是你必须保证这些都成立——所以它不吃”次数”(*had been trying to kill it three times 崩,因为三个离散的点画不成一条连续的线)。

最后,如果这一面你只肯带走一句话,带这句:

一般体给你事实,进行体给你画面,完成体给你后果,完成进行体给你过程和它的代价。

而全书要你记住的那件事,在这一面再次得到验证:这几句中文翻译几乎一样,英文思维却完全不同。 中文把”正""还""一直""过""已经”挂在动词外面,可加可省;英文把这些焊进动词的骨头里,你不选也得选。钻石转了一个面,事件核一动没动,句子已经换了一副面孔。


思考题

第 1 题(辨析)

下面三句都跟 11 月 12 日凌晨那条时间线有关。请回答:哪一句明确表示他成功中止了迁移?哪一句暗示他没成功?为什么第三句不能把 for 换成 in?

a. Priya killed the migration in under a minute.
b. Lin tried to kill the migration for ten minutes.
c. The error rate climbed for twenty-three minutes.

参考答案

a 明确表示成了。in under a minute 问的是”这个事件跑完花了多久”,只有有界短语才答得上来。kill the migration 的终点就是那条连接断掉、orders 表上的锁被释放的那一瞬——2:47。走到即终止条件满足,所以 in 能过,而且一旦 in 能过,就承诺了”到了终点”。

b 暗示他没成功。这句是全组最反直觉的:kill the migration 明明有界,为什么换成 for 就变味了?因为 for 只承诺”在这段时间里,过程在展开”,它不承诺终止条件被满足。你说”他试着中止这条迁移,试了十分钟”,听者默认你要接着说下半句——而下半句就是”然后他升级给了 Priya”。语言学上这叫有界短语的未完成解读(imperfective reading):有界提供了终点,但持续/重复的视角让你停在了终点之前。顺带说一句:这也正是为什么复盘会最后定性为”流程缺失”而不是”Lin 失职”——tried ... for ten minutes 这个形态本身就在说,他不是不做,是做不到。

c 不能把 for 换成 in:*The error rate climbed in twenty-three minutes. 之所以崩,是因为 climb 本身无界——没有”爬完”这回事,你问”爬完花了二十三分钟”答不上来。要让它过 in 测试,你得给它一个终点:The error rate climbed to 34% in twenty-three minutes.——加了 to 34%,终止条件有了,in 立刻就顺了。(除非你把 in twenty-three minutes 强行读成”二十三分钟后它开始爬”,那是另一个意思,而且很别扭。)

一句话收口:in 和 for 不是”花时间”和”持续”的同义词,它们是一对探测有界性的探针。 中文里”错误率涨了二十三分钟”和”错误率二十三分钟涨到了 34%“的差别,靠的是”到”这个字;英文靠的是介词。

第 2 题(辨析)

a. Marcus was careful with the wording.
b. Marcus was being careful with the wording.
c. *Marcus was being tall.

请解释:a 和 b 的差别到底是什么?为什么同样一个 being,b 成立而 c 崩掉?如果换成 Marcus was being evasive,读起来会是什么味道?

参考答案

a 是在给 Marcus 贴属性标签:他是个措辞谨慎的人。这是他的职业素养,是”编译期常量”,不可切换。这句跟这次事故其实没什么关系——他上个月写给别的客户的邮件也是这个风格。放在事故叙事里,a 是一句背景交代,它把镜头拉远,让读者觉得”哦,他一向如此”。

b 是在描述他当时给自己安排的动作:凌晨三点半,屏幕上摊着 51 分钟的事故时间线,八万美元的损失,两百多家门店——他一个字一个字地掂量”degraded”和”outage”之间选哪个。这份小心是临时的,是他自己选的,也是可以脱下来的:等他睡一觉起来重读这封邮件,他可能又把它改回去了。摄像机停在他”小心”这个行为的内部,你看到的是一个正在进行的、高度自觉的操作,而不是一个性格判断。

c 崩,是因为 be being + 形容词 这个句式有一个硬约束:形容词必须描述可控的、可选择的行为。你不能”选择”做一个高个子。所以 rude / silly / careful / evasive / patient / stupid 都能进这个句式(它们都是可以”装”出来、也可以”收”回去的行为),而 tall / young / intelligent / French / left-handed 全崩(都是属性)。这不是词汇清单,这是一条可推导的规则——你只要问”这个词描述的东西,能不能被’装’出来”,答案就出来了。

Marcus was being evasive 的味道:它不说”Marcus 是个回避问题的人”(那是 was evasive),它说”那封信里他刻意绕开了某个点”——一次性的、可归责的、他本可以写清楚的。在事故沟通的语境里这个区分极其重要:性格不能归责,措辞选择可以归责。而且你注意,这一句一旦成立,读者会立刻开始找”他绕开的是什么”——八万美元那个数字?还是”没有用 gh-ost”这个细节?形态自带悬念。

第 3 题(应用)

你要写一份事故报告(incident report)的”时间线”部分,需要表达这个意思:“Lin 当时正在执行数据库迁移,这时订单错误率开始飙升。”

下面四个写法,请选出最合适的一个,并说明其余三个各自会在读者脑子里造成什么误解。

A. Lin ran the migration when the error rate spiked.
B. Lin was running the migration when the error rate spiked.
C. Lin had run the migration when the error rate spiked.
D. Lin has been running the migration when the error rate spiked.

参考答案

选 B。

理由:报告要的是”一件进行中的事被另一件打断”这个时间关系,而 when 子句要求的正是”撞进过程中间”。要撞进中间,被撞的那件事必须是线,所以主句必须走进行体。was running 提供了这条线,spiked(点动词:错误率在 2:37 冲上 34%)撞进线里——两个形状天然配套(线被点撞断)。

A 的问题在开场就讲过:ran 是封装好的点,外壳是硬的,when 撞不进去。读者会把它读成”他跑了那条迁移(这段已经结束了),这时错误率飙升了”——时间关系乱了,而且乱得很要命:它会让人以为错误率是迁移跑完之后才涨的,这跟根因结论直接冲突。要救 A 只有一个办法,把 when 换成 and then:Lin ran the migration, and then the error rate spiked.——and then 只要求先后,不要求撞进中间,所以成立。(不过这样就丢掉了”两件事重叠”这个关键事实,所以 B 仍然更好。)

C 是过去完成时,机位退到了”错误率飙升”这个点之后往回看,承诺的是”在错误率飙升之前,跑迁移这件事已经完成了”。这跟事实不冲突(迁移确实在 2:14 就开始了),但它把报告的意思扭成了”因为他跑完了迁移,所以错误率涨了”——而在 2:37 那个时刻,迁移根本没跑完,它还在跑。事故的关键恰恰是”未完成”。

D 有两处硬伤。一是时间错乱:has been running(现在完成进行)的机位站在现在(2026 年),而 when the error rate spiked 的机位在 2024 年 11 月 12 日凌晨,两个机位打架,一句里只能有一个。二是 run the migration 是一次性事件,用完成进行体暗示”他从 2024 年一直跑到今天”——整句读起来像那条 ALTER 跑了两年。

额外一点:B 里用 spiked 比 increased 更贴切——spike 是成就动词,自带”瞬间冲上去”的形状,正好撞进 was running 这条线里。这属于第九章(语域与态度)的活儿,但在这里顺手一提:事故报告里的时间线,靠动词形状来区分”背景(线)“和”事件(点)“,读者扫一眼就能重建时间关系,不需要额外解释。

第 4 题(应用)

2026 年,你要给同事发一条工作消息,报告一个还没解决的问题。你想说的是:“我找这个 goroutine 泄漏找了一上午,还没找到。”

下面四句,请挑出唯一合适的一句,并说明另外三句各自会让人误解成什么。

A. I looked for the goroutine leak all morning.
B. I have looked for the goroutine leak all morning.
C. I have been looking for the goroutine leak all morning.
D. I am looking for the goroutine leak all morning.

参考答案

选 C。

C 给了你正好需要的三条信息:过程(一直在找)、长度(一上午)、以及到说话这一刻还没停或者刚停下。第三条是关键——同事读完 C,会自动理解成”这事还没完”,从而给出正确的反应(要么来帮忙,要么别催)。而且 have been looking 自带”这个过程正在消耗我”的味道,你不用说 “I’m stuck” 或者 “I’m frustrated”,形态已经替你说了。这在工程沟通里是实打实的效率。

A 是一般过去时,机位站在事件外面把它压成了点。它给读者的信号是”这件事已经结案了”。同事读完的默认反应是”哦,那你找到了吗?“——你还得再补一句。更糟的是,如果语境里没有明确的过去时间点,A 听起来像在讲一件发生在过去、跟现在无关的事。

B 是现在完成体,all morning 给了长度,但没有承诺”还在找”。B 听起来像在汇报工作量:“我这块儿投入了一上午”。听者会追问结果,因为完成体把事件压成了点——点是不分”在进行”和”已结束”的。你想要的那层”还没完”,B 给不出来。

D 是进行体 + all morning,问题在进行体的机位只停在当下这一刻,它描述的是”此刻我正在找”,而 all morning 要求一段从早上铺到现在的长度——进行体没有”回看”能力,铺不出这条线。要铺出从过去到现在并连着当下的线,必须是完成进行体。另外,D 在母语者耳朵里还带一点”我整个上午都在做这件事(而且可能还要继续)“的计划感,不是你要的抱怨感。

一句话总结:要表达”从过去持续到现在、且影响当下状态”,完成进行体几乎是唯一解。 中文”我找了一上午”一个”了”字全包了,英文必须选形态,而且选错了意思就跑偏。

第 5 题(拔高)

这一题是本章最该花时间的一题,分两问。

(a) 为什么进行体天然带有”可能没完成”的含义?这跟”线没有终点”有什么关系?

(b) 更往深想:中文用”正在""还在”这些词也能表达进行,但中文的”正在”可以省略(“Priya 上线时迁移在跑”完全成立);英文的进行体却不能省(*The migration locked the orders table when Priya joined. 别扭)。为什么?

参考答案

(a) 因为”未完成”不是进行体的副作用,是它的几何推论。

你只要接受”进行体 = 把事件拉成线,并且观察者站在线的内部”,那么”看不到终点”就是必然的:你站在线上,终点在你前方,而摄像机的朝向是拍过程,不是拍前方。所以”未完成”不是额外加的一条语义,它是”内部视角”这个定义的直接后果。

形式语义学给这件事起过一个名字,叫未完成悖论(imperfective paradox)。它的内容是:PROG(e) 为真,不蕴含 e 为真。也就是说,The migration was running(进行体)为真,逻辑上推不出 The migration ran to completion(完成/一般体)为真——事实上它也确实没跑完,2:47 被人杀掉了。母案例里还有一句更狠的:Lin was trying to kill the migration 为真,推不出 Lin killed the migration 为真——这一条不是逻辑游戏,这就是那十分钟里真实发生的事。 这个”推不出”不是语言的缺陷,恰恰是进行体的核心功能:它故意不给你终点,好让另一件事能撞进来。

反过来看,这也解释了为什么”点动词 + 进行体”那么别扭:点的内部是空的,你没法站进一个宽度为 0 的东西里面。而”有界短语 + 进行体”(was running the migration——run a migration 是有界的,终点是跑完)之所以特别有张力,正是因为它既有终点(有界),又不让你看到终点(进行)——这就是”悬而未决”的语义来源。母案例里 The migration was still running when Priya joined. 的全部戏剧性,都来自这个张力:你知道它有终点(跑完就释放锁),你也知道画面停在了终点之前,而你不知道中间还有多久。

(b) 这是两种语言在”信息放哪一层”上的设计差异。

中文是词汇化的语言。“正在""还在”是可选的时间副词,它待在语用层:需要时加上,不需要时省略,靠上下文恢复。中文还有另一个优势——它的”了”承担了有界标记,“这时""的时候""……中”这些成分本身就在暗示进行,所以”正在”经常是冗余的。“Priya 上线时迁移在跑”里的”时”字,已经把”撞进中间”这层关系说完了,再加个”还”只是加强语气。

英文是形态化的语言。体在英文里是一个强制性范畴(obligatory grammatical category):每一个定式动词的限定形式,都必须同时交代”时”和”体”。你没法”不选”。你说 The migration locked,你就已经选了一般体——选了”封装成点、不提供内部”这个解读。没有第三个”我什么都不表态”的选项。

用一个你能立刻懂的类比:中文的体,像 Go 里可选的类型标注(:= 推导,你可以不写);英文的体,像 Go 的静态类型系统(每个变量必须有类型,只是有时被推导出来)。 你可以不显式写 var x int,但 x 一定有类型,而且这个类型会约束它能做什么。英文的体就是这样——你可以不”想”它,但你一定会”用到”它,而且选错了就会出错。

再往深一层:这反映的是两种语言对”明确性的代价由谁承担”的不同权衡。英文选择把体焊进形态,代价是说话人必须每次都做一次强制选择(形态负担),收益是听者不需要推理(解码成本极低)。中文选择把体放在语用层,代价是听者要靠上下文推断(解码成本略高),收益是说话人不用每次都表态(形态负担为零)。

没有优劣,只有取舍。但对你这个中文母语者来说,这个取舍的后果非常具体:你的中文直觉会告诉你”这个信息可省”,而英文的强制形态告诉你”这个信息必填”。 你之所以在 locked 和 was running 之间犹豫,往往不是因为你不知道区别,而是因为你的母语从没逼你做过这个决定。

而这套”强制选择”的逻辑,在英文里远不止体这一处。下一面我们会看到它在另一个维度上重演:英文连”谁当主语”都不允许你弃权——你必须指定一个,而且被你藏起来的那个角色,会决定整句话的道德重量。回想一下本章第四节的那个观察:事故报告的标题写 The migration locked the orders table.,主语是那条迁移,不是 Lin。这不是风格偏好,这是一次道德工程。那就是第三面:语态与主语选择。


附:本面速查表

A. 四种语法体总表

体形态摄像机位置时间形状它回答的问题典型搭配的动词/短语类别母案例例句
一般体
(simple)
locked
runs
事件外面,压成点点(封装)这件事发生了吗?任意;尤其适合有界短语做事实陈述The migration locked the orders table.
进行体
(progressive)
was running事件内部线(无终点)那一刻画面里在演什么?动态、有内部结构的线动词/有界短语;点动词需复数化或改指前段The migration was still running when Priya joined.
完成体
(perfect)
has resolved
had tried
结果处,往回看点 + 尾巴现在(/参照时刻)怎么样了?任意;有界短语给”结果”,无界短语给”经历/持续”The incident has been resolved. No data was lost.
完成进行体
(perfect progressive)
had been trying内部 + 结果处线 + 尾巴到那一刻为止,一直在怎样?持续/可延续的动态短语;配 for / since,不配次数Lin had been trying to kill it for ten minutes.

B. 词汇体(动词形状)速查

分类中文判据例子对进行体对完成体
点动词 punctual瞬间动词无内部,不可展开lock, notice, wake up, escalate, kill, spot, blink, sneeze, die通常不行,除非复数化(orders were failing)或改指前段(was dying)可以,给”发生过”
线动词 durative持续动词有长度,可切run, climb, try, retry, wait, watch, sleep, work, rain可以可以
无界 atelic无内在终点in X 测试失败run, climb, rain, work on, search the logs, walk toward可以只能给”经历”或”持续”,给不出”做完了”
有界 telic有内在终点in X 测试通过kill the connection, finish the migration, climb to 34%, write the runbook section可以,且带”悬而未决”张力可以给”结果/做完了”
状态 stative静态、同质无阶段、无进展know, own, love, be, seem, consist, contain, have(持有义)通常不行;靠临时性救活(is being careful)可以,给”持续”

C. 有界性探针:in vs for

探针问的是对无界对有界
in five minutes跑完花了多久(time-to-completion)失败:*The error rate climbed in twenty-three minutes.通过,且承诺到达终点:Priya killed the migration in under a minute.
for ten minutes持续了多久(duration)通过:The error rate climbed for twenty-three minutes.通过,但暗示未到终点:Lin tried to kill the migration for ten minutes.

D. be being + 形容词 检测器

成立(可控行为,可”装”可”收”)崩(不可控属性,装不出来)
He is being rude.*He is being tall.
You are being silly.*You are being young.
Marcus was being careful with the wording.*Marcus was being intelligent.
Priya was being blunt in the postmortem.*Priya was being left-handed.

E. 完成体的四种用法

用法关心什么判据母案例例句中文
结果现在的状态怎么造成的能改写成”所以现在……”The incident has been resolved. No data was lost.事故已经解决了。没有数据丢失。
经历经验库里有没有这一条区间开放,人还在Lin has run hundreds of migrations since then.从那以后他跑过几百次迁移。
持续这条状态有多长for / since + 可延续短语;且状态现在仍成立Pallet has gone 22 months without a P1 incident.Pallet 已经 22 个月没出过 P1 事故了。
刚刚这条变更的新鲜度just / already / yetMarcus has just finished the customer notice.Marcus 刚写完客户通知。

F. 完成进行体的额外信息(对照表)

句子你看到你看不到
She had cried.她哭过(事实,可能是一小时前)过程、时长、痕迹
She had been crying.她一直在哭,刚停下,眼睛红就是证据终点(可能没停)
Lin had tried to kill it three times.三次离散的尝试连续性、煎熬
Lin had been trying to kill it for ten minutes.一条连续十分钟的线,未停,手在抖终点
*Lin had been trying to kill it three times.——崩:三个离散的点画不成一条连续的线

G. 持续用法的三问检查(写完必做)

检查项问句母案例反例
起点since 后面那个点,真的是状态开始的那一点吗?*Lin has been the on-call training lead since the outage.(他不是事故当天就当上的)
连续性中间断过吗?*The orders table has been locked since 2:14.(2:47 锁就释放了)
现时性这个状态现在还成立吗?同上。要救就得换机位:The orders table was locked for thirty-three minutes.


下一面:第三面:语态与主语选择 —— 为什么”The migration locked the orders table”和”The orders table was locked by the migration”讲的不是同一件事,以及被你藏起来的那个角色(Lin),如何悄悄决定整句话的道德重量。