造一门新语言的代价:五年五百万美元换来的教训

2026-05-14 13 预计阅读时间:1 分钟
来源:oschina.net AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:10 分钟

Wasp 联合创始人 Matija Sosic 最近写了一篇坦白文章——团队花了五年、烧了超过五百万美元,最终承认:从零造一门新的 Web 开发语言,是个错误。

这不是一个"失败创业"的故事,而是一个几乎所有技术人都会面对的选择困境:当你觉得现有工具不够好,是该在已有生态上搭一层抽象,还是另起炉灶造一套全新语法?Wasp 的经历把后者的真实成本摊开了。

新语言为什么诱人

造语言有一种工程师独有的浪漫。你设计语法、定义语义、实现编译器——每一步都像在创造世界。对外讲故事也方便:"我们造了一门语言,颠覆了 X。"媒体喜欢这种叙事,投资人也会多看两眼。

Wasp 想解决的问题很具体:Web 开发全栈配置太分散,前端、后端、数据库、部署各管各的,开发者要在多种语言和框架之间反复切换。Wasp 的思路是造一门 DSL,把整个 Web 应用的声明收拢到一个文件里,再编译生成各层的代码。方向没错,但"造一门语言"这个决定,让成本曲线彻底失控。

隐藏在语法背后的真实账单

Sosic 列出的代价远不止"写了个编译器"这么简单:

工具链是第一道深渊。 新语言意味着新的解析器、新的 LSP、新的 formatter、新的 linter、新的包管理器。每一个都是独立项目,每一个都要持续维护。你不仅要追语言本身的演进,还要追 VS Code 扩展 API 的变更、Node 版本的迁移。这些"基础设施"不产生用户可感知的功能价值,但缺了任何一项,开发者就不会认真采用你的语言。

生态真空是第二道深渊。 没有第三方库,没有社区教程,没有 Stack Overflow 上的问答。用户遇到问题只能看官方文档——而官方文档永远不够。Wasp 早期用户最常抱怨的不是语法不好,而是"出了错不知道去哪查"。

心智负担是第三道深渊。 让开发者学一门新语言,门槛远高于学一个新框架。框架是在他们已知语言上加一层约定,新语言是要求他们换一套思维模型。招聘、培训、团队迁移的成本全部转嫁到用户身上,而用户的时间是最稀缺的资源。

五年下来,Wasp 团队发现自己大部分精力不在"让 Web 开发更简单"上,而在"让这门语言可用"上。

更务实的路径:在已有语言上做 DSL

Wasp 后来调整了方向:不再推独立语言,而是把核心能力做成 TypeScript 之上的声明式 DSL + 编译层。用户写的仍然是 TypeScript 生态内的东西,工具链、类型系统、社区资源全部现成可用,Wasp 只需专注"全栈声明到代码生成"这一件事。

这个思路可以更具体地落地。下面是一个最小示例:用 Python 做一个声明式 Web 应用 DSL,不造新语言,只做一层薄抽象。

# app_spec.py — 用纯 Python 数据结构声明一个全栈 Web 应用
from dataclasses import dataclass, field
from typing import Optional

@dataclass
class Entity:
    name: str
    fields: dict  # field_name -> type_str

@dataclass
class Action:
    name: str
    entity: str
    operation: str  # "create" | "read" | "update" | "delete"
    auth: Optional[str] = None

@dataclass
class Page:
    route: str
    component: str
    actions: list[str] = field(default_factory=list)

@dataclass
class AppSpec:
    name: str
    entities: list[Entity] = field(default_factory=list)
    actions: list[Action] = field(default_factory=list)
    pages: list[Page] = field(default_factory=list)


# ---------- 声明你的应用 ----------
app = AppSpec(
    name="task_tracker",
    entities=[
        Entity(name="Task", fields={"id": "int", "title": "str", "done": "bool"}),
        Entity(name="User", fields={"id": "int", "email": "str", "name": "str"}),
    ],
    actions=[
        Action(name="createTask", entity="Task", operation="create", auth="User"),
        Action(name="listTasks",  entity="Task", operation="read"),
        Action(name="markDone",   entity="Task", operation="update", auth="User"),
    ],
    pages=[
        Page(route="/",       component="TaskList",   actions=["listTasks"]),
        Page(route="/new",    component="TaskCreate", actions=["createTask"]),
    ],
)


# ---------- 代码生成器(示例:生成 FastAPI 后端) ----------
def generate_fastapi(spec: AppSpec) -> str:
    lines = [
        "from fastapi import FastAPI, Depends",
        "from pydantic import BaseModel",
        "app = FastAPI()",
        "",
    ]
    # 为每个 Entity 生成 Pydantic model
    for entity in spec.entities:
        fields_code = ", ".join(
            f'{name}: {typ}' for name, typ in entity.fields.items()
        )
        lines.append(f"class {entity.name}(BaseModel):")
        lines.append(f"    {fields_code}")
        lines.append("")

    # 为每个 Action 生成路由
    method_map = {"create": "post", "read": "get", "update": "put", "delete": "delete"}
    for action in spec.actions:
        method = method_map[action.operation]
        path = f"/{action.entity.lower()}/{action.name}"
        lines.append(f"@app.{method}('{path}')")
        lines.append(f"def {action.name}():")
        lines.append(f"    # TODO: implement {action.operation} on {action.entity}")
        lines.append(f"    pass")
        lines.append("")

    return "\n".join(lines)


if __name__ == "__main__":
    code = generate_fastapi(app)
    print(code)
    # 可写入文件:open("generated_server.py", "w").write(code)

运行方式:

python app_spec.py

输出是一段可直接运行的 FastAPI 代码骨架。关键在于:你没有造新语法。用户写的是标准 Python dataclass,IDE 的类型检查、自动补全、重构工具全部可用。你只做了一件事——把声明式结构编译成目标代码。

如果需要前端生成,再加一个 generate_react 函数即可,逻辑同上。整个"DSL"就是 Python 类的定义,零学习成本。

什么时候才值得造新语言

Wasp 的教训不是说"永远不要造语言",而是说大多数场景下造语言的 ROI 是负的。值得造新语言的条件很苛刻:

  • 目标领域有根本性的语义差异,现有语言的抽象无法自然表达。SQL 是例子——关系代数用通用语言表达非常笨拙,所以 DSL 是合理的。
  • 你已有足够的用户基数和团队资源来支撑工具链的长期维护。Rust 造了新语言,但 Mozilla 投入了大量工程师做编译器、包管理、文档、社区,才让它走出"实验项目"阶段。
  • 你愿意接受 3-5 年的"基础设施期",期间主要产出不是业务功能而是语言生态。

如果这三条不同时满足,更明智的做法是:选一门生态成熟的语言,在上面做框架或 DSL。 把精力集中在用户可感知的价值上——更少的配置、更快的开发、更清晰的架构——而不是花在让编辑器能高亮你的新语法上。

决策检查清单

下次你觉得"现有工具太烂,我要造一个全新的"时,跑一遍这个清单:

问题 如果答案是"否"
目标用户是否已经在用某门语言? 在那门语言上做 DSL/框架
新语法是否表达了现有语言无法表达的语义? 不需要新语言
你是否有 3 年以上的工具链维护预算? 不造语言
新语言是否能让用户 10 分钟内写出可运行代码? DSL/框架更容易做到
你的核心价值是"语言设计"还是"解决业务问题"? 如果是后者,别造语言

Wasp 花了五年才走到这个结论。你不需要重复这个实验。


相关推荐