Skip to content

搞英语 → 看世界

翻译英文优质信息和名人推特

Menu
  • 首页
  • 作者列表
  • 独立博客
  • 专业媒体
  • 名人推特
  • 邮件列表
  • 关于本站
Menu

Lil’ Fun Langs 的勇气

Posted on 2026-03-02

i.lede.me?title=Lil%27+Fun+Langs%27+Guts

我还在琢磨那些小玩意儿。它们是怎么运作的?里面有什么?我需要胰腺吗?如果我不想让我的胰岛素抵抗正常化呢?懒惰算美德吗?

Haskell风格的语言看起来可能很相似,但它们在许多方面却截然不同:

  • 严格 vs. 懒惰
  • 咖喱味 vs. 清淡味
  • 自举式与托管式
  • 解释型与编译型
  • 名义类型与结构类型
  • 漂亮的错误与丑陋的错误

大多数实现都使用标准编译阶段:

  1. 词法分析:源 → 词法流
  2. 解析:词法单元 → 表面抽象语法树
  3. 脱糖:表面AST → 核心AST
  4. 类型推断:核心抽象语法树 → 类型化抽象语法树
  5. 模式匹配编译:类型化抽象语法树 → 案例树
  6. 标准化(ANF/K) :分型 AST → 标准化 IR
  7. 优化:归一化IR → 归一化IR
  8. 闭包转换:归一化IR → 闭包显式IR
  9. 代码生成:Closure IR → 目标(汇编/字节码/C/LLVM)
  10. 寄存器分配:虚拟寄存器 → 物理寄存器(如果是本机寄存器)
  11. 运行时系统:垃圾回收器、原语、入口点

严格型 vs. 懒惰型

严格求值是指在将参数传递给函数之前先对其进行求值。惰性求值是指只有在实际需要参数值时才对其进行求值;求值结果会被缓存,因此最多只执行一次。

 -- lazy eval returns `3` without applying `foo` length [ 1, foo 2, 4 ]
方面 严格(ML,OCaml) 懒惰(Haskell)
正常化 ANF / K-正常形式 STG / thunks 需要
关闭转换 标准扁平扣 关闭 + thunk + 更新框架
代码生成 直截了当 需要执行 eval/apply 或 push/enter 操作
内存管理 数值总是会被​​评估的。 可能包含未经评估的 thunks
尾部呼叫 简单(跳跃) 复杂(进入,更新)
调试 简单(调用堆栈有意义) 困难(发出晦涩的控制流)
运行时复杂度 更简单(约 200 行代码) 更复杂(约 500–2000 行代码 C)

严格求值是最简单的选择。如果想要惰性求值,Peyton Jones 的STG 机器是标准方法。MicroHs 则绕过了 STG 机器,直接编译成带有图归约的组合逻辑。

惰性求值还可以实现无限集合——您可以定义一个无限列表,并且只使用您需要的内容。

咖喱味 vs. 淡味

风格 示例 实施成本
咖喱 Haskell、Ben Lynn、MicroHs 在组合器后端中是免费的;原生后端需要进行参数数量分析,以避免为每个参数分配一个闭包。
平淡 MinCaml、OCaml(内部使用)、Grace、EYG 更简单的代码生成器——多参数函数只是接受元组或多个参数的函数。

在柯里化语言中, fxy表示((f) x) y :即两次函数调用。如果你的后端没有检测到f总是接受两个参数(参数数量分析),那么每次多参数调用都需要进行堆内存分配。

自举式与托管式

我尝试自学吉他,但我教得很糟糕——因为我根本不会弹吉他。

——米奇·赫德伯格

大多数编译器都是用现有语言(例如 C、Rust、Haskell、OCaml)编写的,并依赖于宿主的生态系统来进行解析库、构建工具和包管理。

自举编译器可以自行编译。你用编译器所编译的语言编写编译器,然后使用早期版本的编译器(或最小种子运行时)来构建下一个版本。你的语言变得能够自我维持;编译器本身就是一个测试套件。

有很多优秀的自托管语言值得学习:

  • MicroHs是一个 Haskell 编译器,它将 Haskell 代码编译成组合子。组合子归约器是用 C 语言实现的。编译器本身是用 Haskell 编写的,并且可以自我编译。启动时只需要一个 C 编译器,无需预先安装 Haskell。
  • Ben Lynn首先用 C 语言编写了一个最小运行时环境(约 350 行代码),然后构建功能越来越强大的编译器,每个编译器都使用前一个编译器能够编译的子集语言编写。每个阶段大约需要 100 到 300 行所定义语言的代码。整个过程大约需要 2000 行代码加上 350 行 C 代码。
 C runtime (350 LOC) → compiler₁: lambda calculus + integers → compiler₂: + let, letrec, ADTs → compiler₃: + type inference → compiler₄: + pattern matching → compiler₅: + type classes → ... → compilerₙ: near-Haskell-98
  • Newt是一种依赖类型语言,其编译器是用 Newt 编写的,目标平台是 JavaScript。它通过将生成的 JS 代码检入代码库来实现引导。当目标平台是高级运行时环境(JS、JVM)而非原生代码时,这种方式效果最佳。

解释型与汇编型

解释器通过遍历抽象语法树(AST)或单步执行字节码来直接执行程序。编译器则将程序翻译成另一种语言(例如 x86、C、JS),并让目标平台处理执行。

这里的界限很模糊。字节码虚拟机编译成中间形式。“转译器”则编译成源代码,而不是机器指令。

战略 示例 LOC 估算 权衡
树行翻译员 PLZoo poly ,Eff,Frank,Grace,1ML 50–200 最简单。无需代码生成,无需运行时环境。速度慢(比原生速度慢 10-100 倍)。
字节码虚拟机 OCaml (ZINC)、Tao、PLZoo miniml 200–500 中等价位。便携,速度适中。可编写约 30-50 条指令
本地编译 MinCaml、mlml、AQaml 500–1500 执行速度快,但您需要自行决定寄存器分配、调用约定和应用程序二进制接口 (ABI)。
转译为 C Koka、Scrapscript、Chicken、Austral 200–500 两全其美——既有本地设备的速度,又有 C 编译器完成的复杂部分。
转译为 JS/Go 纽特,SOSML,博尔戈 200–400 Web/生态系统部署,但您将继承目标平台的性能模型
组合子简化 本·林恩,MicroHs 100–300 没有闭包,没有寄存器。C 语言编写的图归约求值器。简单但速度慢。

一些小型趣味语言通常是解释器。无需编译,就可以跳过闭包转换、寄存器分配和运行时系统。从解释器到编译器的飞跃大约需要 500 到 2000 行代码。

名义类型与结构类型

type Meters = Int type Seconds = Int -- Nominal: Meters ≠ Seconds (different names) -- Structural: Meters = Seconds (same shape)
风格 示例 结果
名义上的 OCaml、Haskell、Austral 名称即身份——形状相同并不意味着类型相同
结构 EYG、Grace、TypeScript、Simple-sub 形状即标识——相同的字段/变体意味着相同的类型

大多数 ML 系列语言对于代数数据类型是名义类型的,而对于记录类型(如果已实现)则是结构类型的。行多态(EYG、Grace、Koka)本质上是结构化的——它作用于“至少包含这些字段的任何记录”。简单子类型更进一步:它支持并集和交集类型,并且保留了主类型推断。

漂亮的错误与丑陋的错误

-- Ugly: Error: type mismatch: int vs string -- Pretty: 3 │ let x = 1 + "hello" │ ^^^^^^^^ Error: I expected an `int` here, but got a `string`. The left side of `+` is `int`, so the right side must be too.

漂亮的错误信息并非涂上油漆就能实现的。要指出代码中的某一行/某一段,必须逐个遍历编译器的每个阶段。一个最小可行错误系统:

  1. 源跨度遍历每个抽象语法树 (AST) 节点。每个表达式、模式和类型都包含{ file, start_line, start_col, end_line, end_col } 。这导致每个节点额外占用一个字段。
  2. 通过去糖化保留跨度。当你降低where to let时,新的let节点会继承 ` where的跨度。
  3. 通过类型推断保留跨度。当合一失败时,您需要两种冲突类型的跨度。
  4. 格式错误及上下文。请显示源行,用下划线标出相关段落,并解释错误原因。
质量 示例 成本
榆树层 榆树,澳大利亚 针对每种故障模式定制错误信息。力求做到最好,用户体验最佳。
足够好了 Tao、Ante、OCaml 源跨度 + 通用格式。涵盖 90% 的情况
位置 MinCaml,大多数小型编译器 行号,但没有跨度高亮显示或解释
德布鲁因指数 详述动物园(故意) 变量名丢失——这对研究来说没问题,但对用户来说很糟糕。

莱克星

方法 使用者 LOC 估算 笔记
手写递归 MinCaml(Rust 端口)、Tao、Ante 100–300 完全掌控,最佳失误
ocamllex / mlllex MinCaml(原始)、HaMLet、PLZoo 50–100 OCaml/SML 主机标准
亚历克斯(哈斯克尔) MicroHs,许多 Haskell 托管的 50–100 Haskell 主机标准
解析器组合器(集成) 本·林恩,一些教育 0(解析器的一部分) 无词法分析器语法分析

可选增强功能:

  • 布局/缩进敏感性(Haskell 风格的越位规则):Ben Lynn 在后续的引导阶段实现了此功能。MicroHs 包含完整的布局解析。代码量增加 100-300 行。该算法在Haskell 报告的 2.7 节中有详细描述。
  • Unicode 标识符:大多数小型编译器完全忽略这一点。Koka 支持它。
  • 插入字符串:类似"hello ${name}"这样的语法在 ML 系列语言中不是标准语法,但一些较新的语言添加了它。

解析

解析将扁平的词法单元流转换为树状结构。表面语法被解析为具体语法树 (CST) 或直接解析为抽象语法树 (AST)。ML 系列语言拥有良好的语法,几乎是LL(1)的。

方法 使用者 LOC 估算 笔记
递归下降 + 普拉特/优先爬升 MinCaml(Rust 端口)、Tao、Ante 200–500 最佳错误信息,最易于扩展
ocamlyacc / mlyacc (LALR) MinCaml(原版),HaMLet 100–200(语法文件) 标准但较差的错误恢复
解析器组合器(Parsec 风格) Ben Lynn,MicroHs,PLZoo 100–400 优雅、构图、回溯
PEG / Packrat 在ML家族中罕见 100–300 线性时间保证

后续的每个阶段都会转换这种类型。在 ML 系列语言中,抽象语法树 (AST) 通常如下所示:

 type expr = | Lit of literal (* 42, 3.14, "hello", true *) | Var of name (* x *) | App of expr * expr (* fx *) | Lam of name * expr (* fun x -> e *) (or \x -> e) | Let of name * expr * expr (* let x = e1 in e2 *) | LetRec of name * expr * expr (* let rec f = e1 in e2 *) | If of expr * expr * expr (* if c then t else f *) | Tuple of expr list (* (a, b, c) *) | Match of expr * branch list (* match e with p1 -> e1 | ... *) | Ann of expr * type (* (e : t) *)

名称解析与去糖化

在类型推断之前,表面抽象语法树(AST)被简化:

  1. 字母重命名:每个绑定器都被分配一个唯一的标识符,以消除类型遮蔽。MinCaml 的 Rust 移植版在类型检查期间执行此操作。大多数版本在解析期间或单独的解析过程中执行此操作。
  2. 固定性解析:中缀运算符会根据声明的优先级和结合性重新关联。HaMLet 会单独执行此操作。许多小型编译器会在解析器中硬编码运算符优先级。
  3. 脱糖:表面结构被降低到核心结构中:
    • where子句 → let
    • 模式匹配中的守卫 → 嵌套if
    • do法(单子)→ >>=链
    • 列表推导式 → concatMap
    • 运算符部分 → lambda: (+ 1)变为fun x -> x + 1
    • 记录语法 → 位置构造函数 + 访问器函数
    • 类型类实例 → 字典传递(详述)

类型推断

这是 ML 系列语言的核心。“标准”算法是 Hindley-Milner (HM) 类型推断,具体来说是算法 W或算法 J。

核心组件:

  1. 类型表示:类型是由类型变量、类型构造器和函数箭头构建的项: type ty = TVar of tvar | TCon of string | TArr of ty * ty | TTuple of ty list
  2. 合一:给定两个类型,找到使它们相等的最一般替换。实现方式为对类型变量进行并查集结构体处理,并带有条件检查。
  3. 概括:在let边界处,类型中的自由类型变量被普遍量化,以产生多态类型方案: ∀α. α → α 。
  4. 实例化:当使用多态名称时,其方案会使用新的类型变量进行实例化。
 -- Given: let id = fun x -> x in (id 1, id true) -- Type inference trace: -- 1. id : α → α (infer: x has fresh type α, body is x) -- 2. generalize: id : ∀α. α → α (α is free at let boundary) -- 3. id 1: instantiate α=β, unify β→β with int→γ, get int -- 4. id true: instantiate α=δ, unify δ→δ with bool→ε, get bool -- 5. result: (int, bool)
方法 使用者 LOC 估算 笔记
算法 W(基于替换) 算法 W 逐步详解,PLZoo 150–400 最容易理解的是,积极地进行替换
算法 J(可变引用) MinCaml,大多数生产编译器。 100–300 效率更高,使用可变统一变量
基于约束的(HM(X)) GHC,一些研究编译器 500–2000 将约束生成与求解分离;可扩展
双向类型检查 精细化动物园,一些依赖类型系统 200–500 交替使用检查/推理模式;可扩展至依赖类型

但高级的字体系统功能并非免费:

增强 增加了复杂性 使用者
类型类/特征 +500–2000 LOC Haskell、MicroHs、Ben Lynn(后期)、陶
行多态性(可扩展记录/变体) +300–800 LOC Koka、1ML、EYG、Grace
高等类型 +200–500 行 哈斯克尔,科卡
GADTs +500–1500 LOC GHC,OCaml 4.x+
代数效应(已键入) +500–1500 LOC Koka、Eff、Frank
从属类型(完整) +1000–5000 LOC 精益动物园,伊德里斯,精益
代数子类型(并集/交集) +500 LOC 简单子程序,MLscript
一等多态性(系统 F) +300–1000 行 1毫升,MLF
模块系统(函子、签名) +1000–5000 LOC HaMLet、OCaml、1ML

其他策略:

  • 多态性:仅进行单态类型推断(不使用∀量化)。每个类型都完全确定。这通过完全消除泛化和实例化,将类型检查器的代码量减少到约 100 行。类似id x = x的函数在每个使用点都会获得一个具体的类型。
  • 细化:现代类型检查器越来越倾向于将细化(将表面语法转换为完全显式的核心)与合一(解决类型约束)分开。细化动物园清晰地展示了这一点:每个阶段都是一个包含 200 到 800 行代码的 Haskell 文件,逐步添加功能。
  • 通过字典传递进行类型类脱糖: Haskell 风格的类型类通过将类约束转换为显式字典参数来实现。`sort sort :: Ord a => [a] -> [a]变为sort :: OrdDict a -> [a] -> [a] 。Ben Lynn 的编译器和 MicroHs 都采用了这种方法。

模式匹配汇编

通过推断类型,可以将模式匹配编译成高效的决策树或案例树。

方法 使用者 LOC 估算 笔记
决策树(马朗热算法) 大多数现代编译器,Tao,Ante 200–600 最佳方案——无冗余测试,代码质量高。
回溯自动机 老旧的编译器,简单的实现 100–300 更简单但可能重复工作
嵌套 if/switch (简单) 许多教育编译器 50–100 正确,但在最坏情况下会呈指数级恶化
完全省略 MinCaml,PLZoo poly 0 仅支持基本类型的if/then/else语句
去功能化 一些教育编译器 50–150 一系列带有传递过程的部分函数;更简单但效率较低

关键阶段:

  1. 穷尽性检查:如果匹配项未涵盖所有情况,则发出警告/报错。
  2. 冗余检查:如果模式不可达,则发出警告。
  3. 守卫汇编:守卫增加了一项“回溯”义务。
  4. 嵌套模式扁平化: (Cons (x, Cons (y, Nil))) → 测试序列。

权威参考文献是《编译模式匹配生成优秀决策树》 。Luc Maranget 的算法能够生成在测试次数方面可证明最优的决策树。OCaml 和 Rust 都采用了这种方法。

正常化

-- Before (nested expression): f (gx) (hy) -- After (A-normal form): let a = gx in let b = hy in fab

每个中间值都会被赋予一个名称。每个函数参数都变得无关紧要。let 链中let求值顺序现在是明确的。

标准化策略:

战略 使用者 特点
K-范式(MinCaml 的 ANF 变体) MinCaml及其衍生产品 直接式;用let命名所有中间值
A-正常形式(ANF) Flanagan等人,1993年,许多现代编译器 本质上与 K 范式相同;标准名称
延续传递风格(CPS) Appel’s SML/NJ、Rabbit、CertiCoq 每个函数都需要一个额外的延续参数;所有调用都是尾调用。
没有标准化 本·林恩 类型化抽象语法树 (AST) → 直接用于组合逻辑。适用于图归约,不适用于原生代码生成。
社会保障署直接 Scrapscript 跳过 ANF/CPS;采用 SCCP + DCE 的 SSA IR。其余部分由 LLVM/C 处理。
单子范式 一些依赖类型系统(Bowman,2024) 类似于 ANF,但使用单子绑定而不是 let;在某些优化方面更简洁。

优化

如果程序处于标准形式,优化过程可以简化它。在小型编译器中,优化程度会保持在最低限度——目标是避免运行速度慢得令人尴尬,而不是与 GCC 竞争。

MinCaml 的优化过程(总计约 300 行代码):

经过 LOC(MinCaml) 影响
β还原 约50 内联let x = y in ... x ... → ... y ...
让扁平化(关联) 22 let x = (let y = e1 in e2) in e3 → let y = e1 in let x = e2 in e3
在线扩容 约100 用函数体替换对小函数的调用
持续折叠 约50 3 + 4 → 7
死代码消除 约50 移除当x不在e2中let x = e1 in e2
常见亚表达消除 约50 (在 MinCaml 中是可选的,通过哈希函数实现)

对于小型编译器而言,这六次迭代可以覆盖 80% 以上的优化值。它们会迭代应用,直到达到一个不动点(通常需要 2-3 次迭代)。

超越基础知识:

优化 复杂 影响
尾部调用优化 +50–100 LOC 循环是函数式语言的必备要素;循环本质上是递归调用。
已知呼叫优化 +50 LOC 当调用目标静态已知时,跳过闭包间接寻址。
开箱(专业化) +200–500 行 避免对多态函数的单态用法进行装箱。
锥化 +100–300 LOC 将始终在尾部位置调用的函数转换为局部跳转
需求分析(严格程度) +500–2000 LOC 对于惰性语言:确定哪些参数始终会被求值。
工作/包装器转换 +200–500 行 将严格参数与惰性参数分开,以提高代码生成效率。
森林砍伐/融合 +500–2000 LOC 消除中间数据结构(例如, map f . map g → map (f . g) )
全程序优化 变化 JHC 通过 GRIN 实现这一点;它会消除未使用的构造函数,并进行全局特化。

关闭转换

-- Before: let f = \ x -> x + y -- After: let f = { fun = \ env x -> x + env.y , env = { y = y } }

优化后的中间表示仍然包含带有自由变量的函数。闭包转换会将所有函数“封闭”——因为硬件无法理解词法作用域。每个函数都会变成一个二元组:(代码指针,环境记录)。环境记录会在函数定义时捕获其自由变量。

方法 使用者 权衡取舍
平扣 MinCaml、OCaml、大多数编译器 环境是一个包含捕获值的扁平向量。访问复杂度为 O(1),每个闭包只需一次内存分配。标准选择。
链接/共享闭合 一些较旧的Scheme编译器 环境是一个帧的链表。闭包之间共享结构。分配更多内存,访问速度更慢。
Lambda提升 GHC(选择性地),一些教育编译器 通过添加额外参数,完全消除了闭包。闭包本身无需堆内存分配。但调用者必须传递更多参数,并且调用点必须更新。
去功能化 雷诺兹(1972),MLton 将高阶函数替换为基于求和类型的一阶分派。完全消除函数指针。需要进行全程序分析。
组合逻辑(括号抽象) 本·林恩,MicroHs 将 lambda 表达式替换为 SKI 组合子(或其变体)。不使用闭包,也不使用环境。通过图简化进行评估。

代码生成

代码生成完全取决于您选择的目标平台:

目标 使用者 LOC 估算 权衡取舍
本地汇编(x86-64、ARM 等) MinCaml、mlml、AQaml 300–800 最佳性能,最多工作量,平台特定
C 源 Koka、Scrapscript、Chicken、JHC、Austral 200–500 可移植,利用了 C 编译器的优化器,但间接寻址
LLVM IR Ante、gocaml、Harrop 的 MiniML 200–500 原生性能好,跨平台,但依赖项较多
起重机 MinCaml(Rust 移植版),以及一些新语言 200–500 编译速度比 LLVM 快,代码生成效果好,原生支持 Rust。
字节码(自定义虚拟机) OCaml(ZINC 机器),PLZoo miniml 200–500 便携、简单,但执行速度较慢
JavaScript / Wasm MinCaml-wasm、SOSML、Newt、各种 200–400 Web部署,但性能模型有限
前往来源 博尔戈 200–500 继承 Go 的生态系统、工具和并发模型
组合逻辑 本·林恩,MicroHs 100–300 无需分配寄存器,但执行速度较慢
规范化器(无运行时目标) 达尔 200–500 “编译”= 简化为范式。无可执行输出

寄存器分配

程序可以使用任意数量的变量,但CPU的寄存器数量是固定的。寄存器分配决定了哪些变量存储在寄存器中,哪些变量溢出到内存中。

如果目标是原生汇编,则需要自行实现。如果目标是 C/LLVM/Cranelift 等,后端会自动处理这些。

方法 使用者 LOC 估算 质量
图着色(Chaitin-Briggs) MinCaml,Appel 的教科书 200–500 大多数情况下都是最佳选择,标准配置
线性扫描 一些即时编译器,简单的编译器 100–200 编译速度快,但代码质量略差。
天真(什么都说了) 一些教育编译器 50 正确但糟糕的表现
不适用 面向 C/LLVM/字节码的编译器 0 委托给后端

运行时系统

最简配置包括:

成分 复杂 笔记
入口点/堆栈设置 10–30 LOC C 设置初始堆指针和栈指针
垃圾收集员 100–1000 LOC C 见下文
基本操作 50–200 LOC C/asm 输入/输出、数学运算、字符串操作
分配程序 10–50 LOC 提升分配器(如果垃圾回收器处理垃圾回收)
闭包表示 代码生成器的一部分 闭包在内存中的布局方式

一些有趣的编程语言会频繁分配内存——每个闭包、每个 cons 单元、每个局部变量调用都会分配内存。如果没有内存回收机制,内存很快就会耗尽。你需要防止内存垃圾堆积:

战略 使用者 复杂 笔记
无垃圾回收(内存泄漏) 一些教育编译器,MinCaml 基准测试 0 适用于短期项目
切尼复制(半空格) 许多小型编译器,Appel 的教科书 100–300 LOC C 简单、快速,但占用两倍内存
标记清除 各种各样的 100–300 LOC C 不移动物体,无需转发
参考计数 科卡(珀尔修斯),鲤鱼,雨燕状 200–500 LOC 无需暂停时间;Perceus 通过编译时插入精确实现这一点,且没有任何额外开销。
基于区域的 MLKit,一些研究语言 300–1000 LOC 编译时内存管理,无垃圾回收暂停
竞技场/堆叠 非常简单的编译器 20–50 LOC 在竞技场中分配,全部免费
所有权/仿射类型 锈迹、鲤鱼、瘦肉 4 0(编译时) 无需运行时垃圾回收,但限制了语言

如果你的语言具有代数效应(例如 Eff、Frank、Koka 或 Ante),则运行时需要支持分隔延续或 CPS 转换后的调用约定。效应处理程序本质上需要第二个栈或分段栈来捕获延续。Koka 通过证据传递来处理这一点;Eff 和 Frank 则使用解释。

原文: https://taylor.town/scrapscript-001

本站文章系自动翻译,站长会周期检查,如果有不当内容,请点此留言,非常感谢。
  • Abhinav
  • Abigail Pain
  • Adam Fortuna
  • Alberto Gallego
  • Alex Wlchan
  • Alin Panaitiu
  • Anil Dash
  • Answer.AI
  • Arne Bahlo
  • Ben Carlson
  • Ben Kuhn
  • Bert Hubert
  • Big Technology
  • Bits about Money
  • Brandon Skerritt
  • Brent Simmons
  • Brian Krebs
  • ByteByteGo
  • Chip Huyen
  • Chips and Cheese
  • Christopher Butler
  • Colin Percival
  • Cool Infographics
  • Dan Sinker
  • David Walsh
  • Dmitry Dolzhenko
  • Dustin Curtis
  • eighty twenty
  • Elad Gil
  • Ellie Huxtable
  • Ethan Dalool
  • Ethan Marcotte
  • Exponential View
  • FAIL Blog
  • Founder Weekly
  • Geoffrey Huntley
  • Geoffrey Litt
  • Greg Mankiw
  • HeardThat Blog
  • Henrique Dias
  • Herman Martinus
  • Hypercritical
  • IEEE Spectrum
  • Investment Talk
  • Jaz
  • Jeff Geerling
  • Jonas Hietala
  • Josh Comeau
  • Lenny Rachitsky
  • Li Haoyi
  • Liz Danzico
  • Lou Plummer
  • Luke Wroblewski
  • Maggie Appleton
  • Matt Baer
  • Matt Stoller
  • Matthias Endler
  • Mert Bulan
  • Mind Matters
  • Mostly metrics
  • Naval Ravikant
  • News Letter
  • NextDraft
  • Non_Interactive
  • Not Boring
  • One Useful Thing
  • Phil Eaton
  • PostHog
  • Product Market Fit
  • Readwise
  • ReedyBear
  • Robert Heaton
  • Rohit Patel
  • Ruben Schade
  • Sage Economics
  • Sam Altman
  • Sam Rose
  • selfh.st
  • Shtetl-Optimized
  • Simon schreibt
  • Slashdot
  • Slava Akhmechet
  • Small Good Things
  • Steph Ango
  • Stephen Wolfram
  • Steve Blank
  • Taylor Troesh
  • Telegram Blog
  • The Macro Compass
  • The Pomp Letter
  • thesephist
  • Thinking Deep & Wide
  • Tim Kellogg
  • Understanding AI
  • Wes Kao
  • 英文媒体
  • 英文推特
  • 英文独立博客
©2026 搞英语 → 看世界 | Design: Newspaperly WordPress Theme