原文:Everything is an X

“万物皆 X” 是一种更上层的模式,被应用于多种系统的设计中,包括编程语言和用户接口。它有很多优点,也有一些缺点。接下来我们讨论其中的一些,再看几个例子,以帮助理解。

当我们说“万物”时,指向是比较松散的——系统里的很多事物并不是“X”的实例,而是更低层或者完全不同的事物。“万物皆 X”就是指“系统里有惊人数量的事物是 X 的实例”

优点

  • 对实现者简单。不需要为系统中每一个新事物设计和实现一个新的接口,取而代之的是设计一个可以四处重用的接口,只在必要的时候重新定义。
  • 对用户简单。用户只需要学习一个事物,在一个领域增加的知识可以立即迁移到许多其他领域。
  • 元能力。不只是用户能将从一个实例中学习到的知识、技能运用到另一个领域,“X”接口常常可以被提升到更高一个层次进行运用,从而赋予用户超能力——通过再增加一点必要的知识,用户就能从操纵 X 迁移到操纵 元-X。当元也存在若干层时,这种模式的另一种名称为“X all the way down”。
  • 优雅而富美感。除了实用性,这样的系统设计也具有美学价值。

缺点

  • 强行让系统中某些事物的行为符合“X”的定义,会产生糟糕、古怪的适配,可能造成:
    • 能力不足的接口,需要以某种方式来补救。
    • 能力过强的臃肿接口。
    • 使得“X”过于宽泛而导致无用。
  • 给用户更高层次、更元级别的接口可能是在给自己制造麻烦,束缚你重构或者优化内部,因为开放给用户的内容太多了。
  • 当你出于务实的原因打破了“万物皆 X”模式时,就在设计中留下了一些丑陋的角落。

例子

编程

Java:万物皆类

为 API 如何工作提供了一种确定的一致性,减少了学习一个新库的工作量。

在元级别,Java 提供的能力有限,它提供了一些反射能力让类能够表现得如同运行态的对象,尽管并不深入(相比 Python)。

Java 的基本类型打破了自己万物基于类的模式,意味着开发者必须处理装箱、自动装箱这些事情。Javascript 的 Number 类型也有类似问题。

Python:万物皆对象

注意这和 Java 版本的万物皆类/对象非常不同,有更多的事物成为真正的运行时对象,包括函数、方法、类型/类和模块,以及类实例。这造就了 Python 里参数化这种极其强大和通用的模式。

你同样能从元类中获得超能力:

  • 实例皆对象
  • (产生实例的)类皆对象
  • (产生类的)元类皆对象

所以元类编程仅仅是一种正常的编程方式。能通过 REPL 实现,也能通过你已经掌握的任何其他技术实现。使用元类编程无疑需要学习新东西,但工作机制都是类似的。感觉上,Python 就像是由 Python 构成。

这同样有缺点。例如,Python 的 integer 类型是完完全全的 Python 对象,比紧凑表示方式(如 C)浪费内存。而且它们是不可变的,做一点简单的调整就会引发内存重分配。从效率的角度来看,非常糟糕。

[Correction – in depends on implementation; CPython caches integers between -5 and 255 and has some other optimisations, but still has a lot of overhead in terms of the size of the object]

ML语言家族(比如 Haskell/Ocaml):万物皆表达式

例如,没有 if 语句,只有 if 表达式。赋值也一样,是表达式——例如 let 不是一个语句而是一个表达式,定义了本地值并返回 ‘body’ 的值。

Exceptions: Top level assignments, and type signatures. At least in Haskell, the type system feels quite separate. Due to this, AFAICS the meta level is kind of missing in Haskell. Haskell “super-powers” come from a bunch of different features, such as deriving and TemplateHaskell, and programming at type level, especially with various language extensions. These are all cool features, but they have to be learned separately, and I think this is what gives Haskell the feeling of being a big and intimidating language. 这段在下懒得翻了😴

Lisp:万物皆 S表达式

相比 Haskell,Lisp 走得更远,它包含了顶层语句。这种语法上的一致性为编写宏提供了巨大的帮助,也许是 Lisp 可以吹嘘的最大的元能力。

用户接口

Emacs:万物皆缓冲

这是 Emacs 大想法之一——它只有少量不同的 UI 元素,因为几乎所有内容都在缓冲里。和准备编辑的文件的文本内容一样,你同样有:

  • 帮助手册
  • 针对你正在使用的缓冲自动生成的帮助(例如列举所有的键盘快捷键)
  • 搜索结果(这是我的递归搜索工作流中的关键部分,记住上下文)
  • 其他更多的事

一旦学会如何导航和使用缓冲,你能知道更多。

当然,存在其他的 UI 元素,如工具栏、菜单栏,使 Emacs 对新手不那么恐怖。但老手通常会关闭它们——因为没用…它们不是缓冲。

你同样能获得元能力:所有缓冲组成的列表同样是一个缓冲,你可以正常操纵缓冲的缓冲(比如删掉一行等同于干掉一个缓冲)

RDMSs:万物皆表格/关系,可以查询

通过一些具体实现来看看,如 Pg,和你创建的关系一样,内部结果也呈现为关系,包括表/关系/索引的列表和运行信息。

你能获得关于所有表的一张表:

SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
ORDER BY table_name;

查询当前正在执行的查询

SELECT * FROM pg_stat_activity;

And many other things. These things benefit from all the usual powers of SELECT, such as joins, filtering, ordering etc.

如果扩展了元能力,就可以通过对所有表的表进行添加、移除、选择来创建、删除表,同理,可以通过对所有行的表进行添加、移除、选择来操纵行。你的 schema 变成了数据,你的能力变成了超能力。我不清楚是否有数据库实现了这个,或者这样做的优点能够真实超越通常的 DDL。

Visidata:万物皆表(sheet)– 这段在下也懒得翻了😴

At points this seems to go further than PostgreSQL in terms of giving you meta-powers and sticking to the pattern. In addition to the normal data sheets:

…and more.

I’m loving the power of Visidata, which is basically “Excel as a text user interface”, and I’ve realised that a lot of its power comes from its lack of power.

In Excel, you can have multiple tables in a sheet, and put them wherever you like. You can have headers, or not, you can have a different format or data type for every cell etc. In Visidata, you have exactly one table per sheet, you must have headers, each column has exactly one data type etc. The enforced uniformity of “Everything is a sheet (and a sheet has a tightly defined structure)” is actually crucial to making Visidata so much nicer than Excel for many tasks — without it, most of the interface shortcuts and meta-powers would fail.

REST:万物皆资源(resource)

把系统中所有东西都当作资源,并通过一些很好理解的动词(GET、PUT、DELETE 等)来操作,是 REST 规范中一个很有吸引力的部分。

REST 也有一些元能力 —— 在 OpenAPI 里,资源的列表也表现为资源。

相对频繁地使用 REST,我觉得我经常遭遇它不合适的缺点。有时候把某些操作建模为“操纵一个资源”很滑稽,我们实际需要的仅仅是一个 RPC(例如,在 clone/copy/move 中有你想要操作的隐藏属性)。

Unix:万物皆文件(file)

这是一个相当成功的抽象,但是同样也有一点不完美的地方——例如,stdout、stdin 是“文件”,但当我们通过终端操作它时表现得并不像文件。即使在你打算把非文件当做文件对待之前,单纯的文件也比你想象的难搞。某些情况下,你可能希望把文件当作内存而不是文件来操作。

问题

我确信该模式存在比我列举的多得多的例子,我只是展现了冰山一角。EverythingIsa on c2.com has a longer list, for example, but without much analysis. What are your favourite examples? Are there examples where it works very well — or very badly? Are there other advantages/disadvantages that I’ve missed? 这段在下也懒得翻了😴

讨论链接

ps:元能力和最近项目中设计元标签的想法一致,通过元达到形式上的统一。

2022.8.12 被原文作者置顶了,明天我好好调整一下翻译

分类: CODE

2 条评论

ivis · 2023年7月19日 上午2:59

I am curious to find out what blog system you happen to be utilizing? I’m experiencing some minor security problems with my latest website and I would like to find something more risk-free. Do you have any solutions?

    顽石 · 2023年7月20日 上午9:15

    Little information obtained on your comment. You can describe your problems in detail. In addition, stackoverflow is also a good place for help when you are confused.

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注