调查奥丁和齐格

2021-08-08 21:49:14

在我关于 Zig 语言的帖子之后,我被指向了 Odin 语言。从表面上看,奥丁和齐格非常相似,但他们在行为和心态上有一些根本的不同。我在这里写的大部分内容都是基于对 Odin 语言文档和这篇博客文章的粗略阅读。 Odin 在条件编译方面有一个很好的观点。在编译时评估的 if 语句很难区分。我更喜欢 Odin 的 when 子句,但 Zig 也有 comptime if,这使它更容易。我在 Zig 中使用此模型的实际问题是,很容易遇到这样一种情况:您编写的(新)代码不会被调用,但 Zig 会检测到它未使用并且不会费心编译它。当您实际尝试使用它时,您会遇到许多需要修复的编译错误。这与我通常的工作方式形成鲜明对比,即几乎总是让代码处于可编译状态,并依靠编译器来仔细检查我的工作。除此之外,我与博客文章和 Odin 语言的作者 Ginger 存在严重分歧。我只想从那篇文章中提取我认为最重要的几点:我从来没有让程序导致系统在真实软件中耗尽内存(人工压力测试除外)。如果您在低内存环境中工作,您应该非常了解其局限性并做出相应的计划。如果您是台式机并且内存不足,请不要尝试从恐慌中恢复,退出程序甚至关闭计算机。至于其他机器,请相应计划!这与自动堆分配有关(可能会失败,这通常会终止进程,因为没有好的方法报告它)。我对此的反应是“640KB 足够了”,对吗?首先,我以编写数据库为生。当用户使用大小为 100 GB 的数据库时,我在 128 MB 的容器上运行我的代码。即使在合适的服务器机器上运行,我几乎总是不得不处理大于内存的数据集。每次启动程序时,几乎都会发生内存不足的情况。稳健地处理这种情况对于构建系统软件很重要。在这种情况下,在我看来,相应的计划并不是使用一种会让我陷入困境的语言。这不是理论上的,这是我们必须处理的真实情况。 …我对基于异常/类似异常的错误的问题不是语法,而是它们如何鼓励错误传播。这种鼓励促进了一种将错误向上传递给“其他人”来处理错误的文化。我讨厌这种文化,我不想在语言层面鼓励它。在那里处理错误,然后不要将它们传递到堆栈中。你弄得一团糟;你清理它。

我一开始真的不知道该怎么回答。在很多情况下,这甚至没有意义,甚至都不好笑。考虑一个场景,我需要调用一个可以为我计算一些值的服务。我这样做是作为 gRPC over TCP + SSL。让我数一数这里可能发生的错误数,好吗?我的调用服务的代码需要能够处理任何/所有这些。可能还有很多我没有考虑到。尝试构建这样的东西是繁重的、脆弱的,而且实际上行不通。就此而言,如果我为服务传递了错误的 URL,那么执行 gRPC 调用的代码是什么,但会冒泡错误?如果 DNS 返回错误,或者存在证书问题,您如何清理它?唯一合理的做法是提供尽可能多的上下文并将错误报告给调用者。在构建健壮的软件时,将它冒泡以便调用者可以决定做什么不是回传,这是最佳实践。您只需要查看 Erlang 以及对可靠性要求最高的应用程序是如何构建的。它们注定会失败,错误处理和恢复发生在专用(主管)位置,因为这些位置具有正确的上下文来做出实际决定。然而,这的致命影响是 Zig 有明确的错误概念,而 Odin 依赖于多返回值系统。我们已经看到 Go 有多好。事实上,Go 最常见的问题之一是进行正确的错误处理需要多少手动工作。但我认为这里的关键问题是错误作为语言的第一类方面为我们提供了非常强大的能力,errdefer。这个单一的语言特性是我认为 Zig 是一种了不起的语言的原因。一流错误的概念与 errdefer 相结合,使构建复杂结构变得更加容易。请注意,我正在打开一个文件,将它映射到内存,验证它的大小,然后它具有正确的哈希值。我使用 defer 来确保我清理了文件句柄,但是返回的内存呢,在这种情况下,如果有错误,我想清理它,但不是其他情况。考虑如何在没有 errdefer 的情况下编写此代码。我必须在我想要返回错误的两个地方添加“关闭地图”部分。如果我使用多个资源会发生什么,我可能需要做一些需要文件、网络套接字、内存等的操作。这些操作中的任何一个都可能失败,但我只想清理它们失败。否则,我需要将它们返回给我的来电者。使用 errdefer(它依赖于常规返回和错误之间的明确区别)将确保我没有问题。一切正常,我必须记住的状态数量大大减少。

另一方面,考虑一下在 Odin 或 Go 中你会如何处理,你会看到错误处理如何成为一个巨大的野兽。有明确的语言支持来帮助实现这一点真的很好。