cover of episode Writing a shell in Go

Writing a shell in Go

2024/11/6
logo of podcast Go Time: Golang, Software Engineering

Go Time: Golang, Software Engineering

Chapters

Introduction to the podcast and the topic of writing a shell in Go, featuring guest Qi Xiao.
  • The episode discusses the challenges and considerations in writing a shell in Go.
  • Qi Xiao, the guest, shares insights on the complexities involved in such a project.

Shownotes Transcript

让我们开始吧!欢迎来到 Go Time,一个涵盖 Go 社区各方面讨论的节目。如果您喜欢这个节目,您一定会喜欢 Changelog。它会在星期一发布新闻,星期三和星期五进行深入的技术访谈,以及周末的精彩访谈节目。搜索 Changelog,即可在您收听播客的任何地方找到我们。感谢我们的合作伙伴 Fly.io,让您可以在全球范围内不到五分钟内部署您的应用程序。访问 fly.io 了解更多信息。

好的,有什么事吗?

我是 Curt,Fly.io 的联合创始人兼首席执行官。我们热爱云计算的魔力。

我认为了解云计算的魔力对于为用户构建更好的功能非常有价值。基本上,如果您了解这一点,那么现在人们正在进行 LLM 工作,您可以做很多事情。

所以,当您说云计算并非魔法,因为您正在为开发人员构建公共云,并详细解释其工作原理时,这意味着什么?

从某种意义上说,这意味着所有这些都来自某个地方。在云计算出现之前,我们有一个更简单的时代,我们自己搭建服务器,将文件放在服务器上,并运行 Web 服务器以将它们提供给用户。云计算并非魔法。此外,还有更多更复杂的方法来完成相同的事情,以满足许多人的需求。

我认为人们错失的一件事是,AWS 和 GCP 创建了如此大的黑箱子,例如 Lambda,它们实际上是黑箱子。您无法像拿起一块土地一样看到它在外部是如何工作的。您只能使用它。

但是,早期就像土地一样,并没有那么复杂。云计算是现代化的大型虚拟机,用于处理来自用户的请求,并让用户暂停和恢复,从而释放物理计算能力。了解云计算的工作原理很有趣,因为它可以让您为用户构建各种功能。

您可能永远不会想到,我们对如何处理用户代码的考古版本是,我们决定公开机器的概念,这是一种更低级别的交互方式,您可以用来构建 Lambda。机器只是旨在快速启动或快速停止和重新启动,或者旨在暂停和恢复(就像您的笔记本电脑一样)的虚拟机。我们发现,让用户接触到这些基础设施,实际上创建了一些以前无法构建的应用程序,特别是因为我们深入到基础层,并在 Linux 内核功能之上创建了一个非常精简的抽象。我们平台的大部分功能实际上只是公开了一些 Linux 内核功能,我认为这很有趣。但是,您仍然需要了解...

如何充分利用它们。好的,体验 Fly 的魔力,了解 Fly 的秘密,因为他们希望您这样做。他们希望分享 Fly 云计算(用于生产开发人员的云计算,用于开发人员发布的云计算)背后的所有秘密。

了解更多信息。免费试用 fly.io。再次,fly.io。

大家好,大家好,大家好,欢迎收听 Go Time 的另一期节目。我是 Johnny。今天我有一个非常特别的嘉宾,他踏上了一个非常有趣的旅程,一个我个人会觉得非常具有挑战性的旅程。这个人,这个人决定自己编写一个 shell。

是的,就像 S、H、E、L、L 一样,他决定自己编写一个 shell,并且使用 Go 实现了它。欢迎来到 Go Time。我们从哪里开始?在所有可能的副项目中,为什么选择 shell?

对吧?我认为我大约十一年前开始开发 Elvish。当时,您拥有的替代方案是 bash,它一直是 bash。还有 zsh 和 fish。

鱼在很长一段时间内都是孤军奋战,我只是觉得所有这些 shell 都有点局限。这是人们一直在使用的工具,这是一个有趣的东西。

它既是一种编程语言,也是一种用户界面。

人们过去使用操作系统的方式,这就是为什么它被称为 shell,而不是系统内核。我认为设计和实现编程语言很有趣。而且,就像做其他事情一样,这也很有趣。因此,我认为 shell 会成为一个很好的、有趣的项目。

您一直在开发 Elvish,对吧?这就是您为您的 shell 命名的实现。您一直在...

足够长的时间。

十一年有多久?哇。哇。这是否是因为这是您谋生的另一件事?所以,这并不是因为在东京待了七年才得到一个工作的 shell,这只是因为您一直在...

如果已经这么长时间了,对吧?是的,我的意思是,我开始这个项目时还在大学,所以当时有更多空闲时间。

但是,当我找到一份好工作后,事情就稍微少了一些。最近我一直在专注于 Elvish。

嗯,如果我回到全职工作,那么花在 Elvish 上的时间就会减少。因此,许多让您获得基本 shell 的开发工作都发生在第一年。

所以我开始每天使用它,大约在开发它半年后。当时我一直在使用 fish 和 zsh 之间切换。但是,在 fish 之后,我有了足够的特性来使用它作为我的默认终端 shell。

所以,现在这是您的默认 shell。非常棒。您需要在 shell 中完成的所有事情都由它完成。正如他们所说,您正在食用自己的“狗粮”。

是的,是的。我明白了。

我明白了。所以我们有 shell,它既是编程语言又是执行环境,或者将所有内容组合在一起。大多数听众应该熟悉这个概念。但是,shell 只是一个执行环境,或者您如何描述它给一个不熟悉的人?

是的,这是一个很好的问题。我认为从纯技术的角度来看,我将 shell 描述为基本上只是一个终端应用程序。因此,它与 ls、cd 或 vi 等处于同一级别。

除了,例如,vi 显然比 ls 更具交互性,对吧?但是它们基本上在同一级别上运行,它们运行命令,写入输出,并且能够接受输入。有很多不为人知的终端 API 事情正在发生,但这并不是这些程序之间本质上的区别。

从技术角度来看,我们可以将 shell 视为另一个终端应用程序,除了 shell 主要用于启动其他应用程序。但是从技术角度来看,它们是相似的。但是人们经常将 shell 与终端混淆,我认为这并非因为人们不熟悉在 Unix 世界中如何完成事情。

但是另一方面,我认为人们在思考 shell 和终端时,会认为它们不是完全相同的东西,而是您工作环境的一部分。因此,从概念上讲,我们可以说 shell 与其他应用程序略有不同,它更像是您的环境的一部分,而不是另一个应用程序。我认为这实际上是一个非常有趣且几乎具有历史意义的现象,就像 Unix 决定以这种方式做事一样,您有命令,您有 shell,它们是两个不同的程序。

但是,很容易想象一个事情并非如此的世界,就像我们所处的 Unix 世界一样。从技术上讲,shell 只是一个终端应用程序,但它在终端应用程序中是一个特殊的存在。

这与通过您的操作系统启动程序,无论是通过命令行还是通过终端应用程序,是一样的。我使用的是 fish,另一个流行的选择,但我相信还有其他选择。这些都是程序。

因此,默认情况下,它们启动 shell。它可能是 bash,也可能是其他 shell。这就是终端应用程序所做的。

但是,当我启动程序时,它在我的 shell 中启动了一个 shell,对吧?我明白了。他并没有使用...

我想避免使用默认值,即 bash。我想使用 zsh,所以安装了 zsh,并将其设置为默认值。这几乎就像终端的后台,而 shell 是我用来与后台交互的前端。

是的,我认为这是思考它的好方法。您可以将终端视为一种图形运行时,只是它们仅支持文本图形。

另一种思考方式是,您可以将终端视为浏览器,而 shell(例如 ls 或 cat)只是浏览器内部的网页,只是终端的功能比浏览器少,因为其中一些功能专门用于这些应用程序,例如终端有时不会进行高级管理。但是,您可以使用 tmux 来实现这一点,对吧?tmux 可以运行其他程序。因此,这就像一个浏览器,您可以在其中运行其他浏览器。

是的,并且...在过去使用 Windows 的时候,在电影中,您知道,您会看到,例如,在电影中,它是否真的有效地代表了黑客文化?如果有人看过老电影《黑客》,其中有 Angela 和其他一些著名人物,他们实际上是在使用终端。

他们正在输入命令。我认为这太酷了,人们是如何成为黑客的,对吧?所以,我在 Windows 上,您启动一个命令程序。Windows 并没有那么酷,您知道。所以,我开始尝试使用 shell,您知道,当时没有 PowerShell,是的,我一直在尝试拥抱 shell 生活,如果您愿意的话。

您知道,我尝试过 Linux,但它仍然很粗糙,您知道,在最好的情况下,每年都会有改进,但事情并没有完全改变。但是最终,我需要我的操作系统让路,这样我才能完成事情。但是我也需要 shell 的功能,所以我选择了 macOS,并且开始更多地拥抱 shell,因为最终,我们将讨论 shell 的能力,Elvish 的不同之处。但是,我注意到,当您学习使用 shell 时,您会经历一些最基本的事情,您过去使用鼠标点击来完成的事情,例如在您的机器上移动,启动程序,找到窗口并导航系统。

您会发现您可以通过输入文本来完成所有这些事情。但是,使用文本输入而不是鼠标点击,您会经历一个最低限度,例如,哇,这太酷了,对吧?就像一个普通人在想,你在做什么?这太疯狂了。为什么你想用这种方式使用计算机?但对于某些人来说,这是一种梦想,对吧?

是的,是的。我认为我们很多人经历过这个过程,对吧?我认为当我第一次接触 Linux 时,我也非常喜欢人们在这个黑窗口中做酷的事情。我总是告诉人们一个例子,例如 ffmpeg,因为当人们想要做一些不是视频编辑但不是那么简单的视频处理事情时,例如,我只想要这段视频的前 10 分钟,或者我想从这段视频中提取音频,如果您在图形界面中,因为您可以想到很多不同的视频处理事情,您尝试找到一个非常、非常具体的工具,它可以处理音频和视频文件。

这并不是说您需要安装一个 100GB 的视频编辑器,然后阅读其手册并完成您想要完成的特定任务。但是,在终端世界中,您只需搜索 ffmpeg 命令,它可能只是一个命令。

您可以做很多事情,因为终端界面是一个抽象层,它位于底层代码之上,对吧?我认为 ffmpeg 的 CLI 可能只是整个代码库的一小部分。但是,如果您构建一个类似 ffmpeg 的应用程序,您需要考虑如何组织菜单,如何放置每个功能。您需要考虑很多事情。但是,在终端中,由于它是一个抽象层,您突然可以更容易地访问更多功能。

大家好,我是 David,Retool 的创始人兼首席执行官。David 确实在内部工具软件开发领域取得了巨大的成功。Retool 的核心思想是什么?

是的,七年前我们开始 Retool。核心思想是内部软件是一个巨大的、未被充分重视的领域。令人惊讶的是,内部软件代表了全球所有代码的 50% 到 60%。

如果您考虑一下,我们大多数人,硅谷的许多公司,都是软件公司,销售软件。因此,大多数外部软件都是物理软件。但是,您是否考虑过世界上大多数软件工程师实际上并不在这些软件公司工作?

这并不是说这些公司中有很多,但至少大多数公司,世界上大多数非软件公司,例如一家销售软件的服装公司,应该有大量的内部软件。所有软件工程师每天都在构建内部软件。

所以,我认为 Retool 的第一个原因是,如果您查看所有这些内部软件,它们非常相似。例如,如果您查看 Zara 或一家饮料公司,这两家公司显然非常不同,但如果您查看它们内部用于运行其运营的软件,它们非常相似。

它基本上是表单、按钮和表格,这些都是可以以不同方式组合在一起的常见构建块。但是,如果您考虑不仅仅是 UI,还考虑许多事情背后的逻辑,例如添加数据库、实施验证和授权,这些都是常见的构建块。

人们正在构建内部工具,这是一种力量。内部软件是一个巨大的、未被充分重视的领域,开发人员正在构建这些软件。我们能否创建一个更高层次的框架来构建所有这些软件?这将非常酷。

这将非常酷。所以,听众朋友们,Retool 适用于所有人,适用于企业,适用于大规模部署,适用于开发人员。如果您想构建自己的内部软件,Retool 是最佳选择。获取演示或免费试用 Retool。再次,retool.com。

那么为什么选择Go来完成这项任务呢?

对吧?所以想想十年前的自己,当时的编程语言环境与现在大相径庭。所以当我第一次开始开发Elvish时,Go语言实际上刚刚出现。

我并不是从Go 1.0开始的。哦,我从1.1开始的。所以它是在那之前半年发布的。

当我考虑是否要编写一个新的shell时,当时我的选择是什么?当时的选择基本上是C、C++、Java,或者像Python这样的更动态的语言,对吧?而Go在当时就像一个非常现代的语言,它比Java更简洁,比C和C++更具表现力。

Go也比Ruby等语言快得多。所以当时Go几乎是理所当然的选择,因为Go在十年前是一个非常现代的语言。嗯,如果现在有人想尝试编写一个新的shell,并决定使用哪种语言,那么选择可能会更困难一些。

因为现在我们有Rust。Rust并不是十年前出现的。我敢肯定,它在开发中有一个0.1版本,但当时它并不为人所知,就像Zig一样。还有很多其他现代且强大的语言,但我仍然选择用Go来完成,因为...

稍后会讨论的,是的,是的。所以这很有趣。如今,大多数人想到编写这样的项目,可能会使用Rust或Zig。

我还在考虑另一个...嗯,刺激。但我认为Go过去常常被贴上“系统语言”的标签。还记得Go的早期,人们常常这样称呼它。我甚至认为Go的Docker项目,我过去常常将Go视为一种系统编程语言。但我认为,随着时间的推移,我发现了一个利基市场,你可以继续做系统编程的事情,但我认为如今大多数人会选择更底层的语言来处理云计算方面的东西,就像Go在云计算领域一样。但是,是的,看着编程语言本身在生态系统中找到自己的利基,这确实是一段有趣的旅程。

嗯,我想顺便提一下,关于系统语言的争论实际上非常有趣,因为我认为Go的最初创建者在谈论系统编程语言时,可能更多地考虑的是分布式系统。想想操作系统。我认为如今Go的利基市场并没有与之矛盾,对吧?因为所有云计算工作都是关于分布式系统的。

但是,现在可能更多地由像Zig这样的语言来处理更接近操作系统的部分。但是,你仍然可以使用Go来完成很多非实时、相对底层的任务。Go拥有非常完整的Linux、macOS和Windows系统调用的绑定。所以你可以做很多底层的事情,只要它不是实时性的,如果你能接受一点垃圾回收,那么你就可以做到。

所以,很明显,能够在所有主要操作系统上进行分布式部署,这确实是一个优势。我肯定认为Go在构建这些功能方面做得很好,并且能够为每个平台提供单一可执行文件。在编写代码时,你是否需要针对不同的目标平台做不同的事情?

是的,但没有你想象的那么多。我针对不同平台做的不同之处在于如何从终端读取键盘事件。因为Windows实际上有一个不同的API来读取键盘输入,它实际上会给你...

所以,如果你对这个主题感兴趣,你可以搜索“终端转义序列”、“键盘”之类的。终端中的键盘输入方式远没有那么直接。如果你输入一个简单的字母,比如a或b,程序看到的就只是a或b。

但是,如果你使用功能键,比如F1、F2、Home或End,你使用并修改了功能键,那么有十种不同的编码方案,不同的终端模拟器用来表示这些键。所以,作为shell,你必须处理这些转义序列,并将其转换为键盘输入,并修改功能键。

但是Windows实际上有一个更合理的API,它会直接告诉你这是哪个功能键,这是哪个修饰键。

所以我使用Windows的API,但现在Windows也支持VT100风格的转义序列,Windows也支持这种糟糕的键编码方式。所以它不再是必需的了。但我喜欢Windows的API更简洁。

所以这是在不同操作系统上代码路径不同的一个地方。从这一点来看,平台相关的代码实际上出奇地少。你可能认为在启动外部程序方面会有不同之处。

但Go实际上在操作系统中为此提供了非常完整的抽象,在`os/exec`包中。它为你提供了一个shell启动外部程序所需的一切。

如果你想想shell是如何启动外部程序的,有些方法可能非常复杂。对吧?你需要控制程序的环境。

你需要控制输出去向。例如,如果在管道中,输出会进入管道而不是终端,或者输出到文件,等等。但Go在`os/exec`中以可移植的方式为你提供了所有这些功能。

所以实际上我并没有为此编写很多平台相关的代码。`os/exec`函数接受的参数中包含一个字段。我不记得确切的名称,但这是唯一需要平台相关处理的部分,这是我需要针对不同平台做的唯一事情。

除此之外,它相当可行。当然,有些事情是特定平台的,例如,创建新文件时应用的独特掩码,它是一个位域,这有其局限性。这些东西显然是平台相关的。

是的,有些东西在Windows世界中并不完全兼容,对吧?所以我们已经讨论了Elvish的一些功能。Elvish在哪些方面超越了像Bash这样的主流shell?

它做了什么?用Elvish来做什么?哇,Elvish非常不同,非常强大。

对吧?这与我决定参加这个节目有关。我主要是因为沮丧。当时我使用过一些传统的shell。

其中一个是在为一些本地Linux服务做一些系统管理,我接触到了数百,不,数千行批处理脚本。我试图维护它,发现这几乎是不可能的,因为它充满了各种晦涩的东西,比如,你必须将变量放在双引号中,对吧?否则,变量中的空格会产生奇怪的行为。

你永远无法弄清楚比较字符串的正确方法。批处理脚本有一个`test`命令,还有一个左括号命令,它们的行为相同,但还有一个双左括号命令,它与`test`命令不同。还有其他一些东西。

所以所有这些细节都非常相似。所以我认为一定有办法编写更合理的shell脚本。但当时我并没有找到很多方法。

我没有找到很多好的shell脚本编写方法。另一个经历是我从Bash切换到Fish,我试图实现目录历史记录。

嗯,那是在出现像所有这些Dash插件之前。当时网上并没有很多现成的Bash插件。所以你必须自己做一些事情。

所以我试图实现目录历史记录,我阅读了很多shell文档,因为它们实际上有一些很酷的UI功能,比如自动补全功能。所以我认为你一定有很好的工具来做一些合理的UI功能。

但是整个UI系统,我也感到有点沮丧,基本上放弃了,因为API真的很难用。

我的意思是,你可以做很多很酷的事情,但说实话,它并不容易使用。所以这两个经历让我想到,首先,我们需要一个感觉像真正的编程语言的shell;其次,我们需要一个具有合理UI功能的shell。所以Elvish的主题是,它具有编程功能,所以作为入门,它很明显地具有列表和映射,并且它们可以嵌套。在传统的shell中,这可能并不存在。

嗯,如果我们回到最原始的shell,比如sh,它唯一拥有的就是字符串,对吧?你可以用空格分割字符串,所以你可以将空格放在字符串中,你可以假装你有列表,但实际上并没有。

一些后来的shell确实有实际的列表或甚至映射,但它们不能嵌套,比如你可以有一个字符串列表,你可以有一个字符串到字符串的映射,但你不能有一个列表列表或映射映射。这在传统的shell中是不可能的。但是Elvish拥有所有这些功能。它就像一个真正的编程语言,你可以用它表示任何东西。Elvish也支持lambda,所以它支持函数式编程。

它是一种非常函数式的编程语言。所以Elvish中的很多代码看起来都像Lisp,因为它拥抱函数式编程。嗯,所以这是其中一点。

真正的编程功能,以及开箱即用的UI功能,我可以举两个例子。一个是它有一个内置的文件管理器。如果我按Ctrl+i,它会显示当前目录、父目录以及你选择的目录的预览。

所以,如果有人使用Ranger,UI非常类似于它。另一个是,在Bash中,你可以按Ctrl+r来搜索你的命令历史记录,对吧?但它一次只给你一个匹配项。Elvish偷了同样的快捷键,但如果你按Ctrl+r,它不会让你一次搜索一个命令,而是给你一个所有匹配命令的列表,你可以过滤这些命令,并查看所有匹配当前过滤条件的命令。

所以它附带了一些内置命令,但你也可以依赖外部命令,就像任何其他shell一样,以及任何其他shell工具。

对吧?所以我会说主要是的,但也有更多关于人们在shell中倾向于使用和需要的命令的考虑。所以它非常像编程语言。

所以,我能想到一个有趣的例子,比如,在Bash中,你有一个`mkdir`命令来创建目录,它非常适合交互式使用,你可以与它进行交互。但在Elvish中,Elvish也有一个内置的`mkdir`命令,它位于一个名为`os.mkdir`的单独命名空间中。但它实际上只接受一个参数,因为它实际上非常像`os`包中的`mkdir`函数。如果你想创建多个目录,你可以编写一个循环。它确实更侧重于编程语言方面,对吧?

所以,你没有试图保持与现有工具的兼容性,对吧?你通常使用`mkdir -p`标志,然后你可以创建整个目录树,但你不需要这样做。你需要创建多个目录,你可以使用循环。

对吧?所以它更侧重于编程。所以,作为入门,Elvish不会阻止你使用任何现有的程序。

所以,在使用Elvish时,我大多数时候仍然使用`mkdir`。我仍然可以使用系统命令。

而且我经常使用它。我仍然使用它作为创建Linux目录的默认方式。

但是Elvish的`mkdir`命令也位于一个单独的命名空间中,就像我提到的,它被称为`os.mkdir`,而不是`mkdir`。当你想编写可以在不同环境中运行的可移植脚本时,它很有用,比如在Linux和Windows上。

哦,我提到过它支持Windows吗?Windows做得很好。他们是怎么做到的?很多时候,这些内置命令在尝试编写这些可移植脚本时非常有用。但是,如果你只是想在你的系统上编写一个脚本,那么你可以使用任何你想要的东西。你不需要使用任何内置命令。

我正在查看模块的文档,对吧?除了内置命令之外。

你知道,你有一些模块用于文档、编辑、包管理器、命令行标志传递、数学等等。所以这些东西,大多数都与目录和文件处理有关,但有些东西,比如字符串操作,如果你需要的话,会很有用。

所以,这里面似乎有系统方面的东西,也有运行时方面的东西,以及一些与之相关的其他东西。所以,这听起来像是你构建了一个工具来处理数千行Bash脚本。你有没有回头修改过任何东西?或者你已经继续前进了吗?这是否让你觉得你应该做一些事情?

是的,我没有回到那数千行的批处理脚本,因为有人维护了那个脚本,但我确实重写了它。Elvish项目中有一些Bash脚本。多年来,我一直在用Elvish重写所有Bash脚本。

这总是改进,对吧?就像你刚才提到的流操作。所以,像`sed`这样的工具确实有流操作功能,但它们通常使用非常晦涩的Bash索引语法。Bash中有一个内置的方法可以从字符串中删除前缀。

你这样做的方法是,如果你的字符串是$s,你做的就是$ {s#prefix}。所以它使用这种非常符号化的语法,大量使用标点符号,我想如果你每天都这样做,你会记住它,你会喜欢它。但如果你每周只这样做一次,你可能不会记住它。

你可能会不断地问自己,这是正确的标点符号吗?但在Elvish中,因为它设计成编程语言,你可以直接使用`str`模块,有一个名为`str.trimLeft`的函数。

还有`str.trimRight`和`str.trimPrefix`。所以,使用这些其他工具,虽然可能比直接编写Bash代码要长一些,但它更易读。如果你是一个Go程序员,这些功能在Go的字符串库中使用相同的函数,并且它们的行为完全相同,因为它们实际上只是使用Go标准库中的函数。

你是否在Elvish中实现了一个特定的功能,让你觉得,哦,这就是我选择Go的原因?这是一种完美的用例?

是的。实际上有两个。一个是刚才我说的,我要写这个,标准库。

Go 的标准库很棒,质量很高,文档也很完善。就像有很多功能,对吧?而且都是很好的功能。

所以,就像你浏览 Elvish 的参考菜单一样。如果你看看,其中一半基本上只是库和库,就像 Elvish 的查找一样。所以 S。

T、R 库只是字符串库。R、E 库用于正则表达式,只是 rex 库。我做了这些。我将名称略微缩短,因为在 Elvish 中,模块名称和变量名称实际上甚至不在同一个命名空间。

所以我能够使它看起来稍微更简洁一些。还有……库的功能,比如 OS 存储库路径。嗯,OS 库基本上就是 OS 库,比如数学库基本上就是数学。

很多标准库的功能都是从……有些功能实际上是免费提供的,因为有非常直接的方法来更改它,以适应它。Go API 到平均值。API。我为此使用了反射。有些需要稍微包装一下,但绝不会太多。

所以,标准库是为什么如果我今天用 Go 做这件事,我仍然会用 Go 的一个非常重要的原因,我认为大多数其他语言都没有这么广泛的标准库。如果你问,我能得到一个 HTTP 库吗?它作为每个库的一部分。我可能会直接使用 Go 的 HTTP 库。

对吧?是的,我只是创建了 Elvish 绑定。是的,如果你去……是的,完全正确。

尽管 HTTP 库处理很多函数,函数绑定稍微有点困难,因为 Go 的函数式编程方式和 Elvish 的方式略有不同。除了标准库之外,另一个有趣的部分,在我刚开始使用 Go 的时候,我没有真正考虑过它。但 Go 是一种垃圾回收语言。

Elvish 也是一种垃圾回收语言。因此,这种巧合的快乐结果是我不必实现自己的垃圾回收器,因为平均值,例如学校值。所以如果一个平均值成为范围 ge,它也会成为范围 ge,这将由 Go 垃圾回收器收集。但是,如果你用 Rust 写一种新的编程语言,而它是一种垃圾回收语言,你必须花时间编写自己的垃圾回收器,或者可能使用现有的垃圾回收器。但无论如何,如果你用 Go 写,你不需要自己的垃圾回收器。

收集器。我一直在思考这些日子,当人们在做编程语言决策时。你知道,这里有一些细微的引擎,但也许这只是我。

但我认为选择使用哪种编程语言,以及为什么你会选择一种语言而不是另一种语言的决策过程,感觉我过去更注重技术,更注重细节,就像你想要寻找特定的事情一样,对吧?知道你是否需要垃圾回收、解释语言或编译语言,或者是否需要垃圾回收,对吧?这些决定,这些似乎是垃圾回收的任何酷炫的东西。

但我认为,对于我们的听众来说,有这么多人正在做,选择语言的过程应该更具批判性,对吧?我应该采取更批判性的方法,用批判的眼光来看待它,对吧?这回到了选择合适的工具来完成工作。你知道,至少从我看到的,大多数有中长期职业生涯的程序员。

我们会选择一些语言,将其放入两个中,并使用适合特定情况的语言。所以当你提到,好吧,我正在编写 Elvish。它有 GC,我可以直接选择它作为底层语言。

GC,对我来说,这就像……好的,这是一个选择语言的理由,对吧?你决定要使用什么工具来构建自己的产品?

是的,当然。尽管我当时在编写时并没有真正考虑过这一点。

是的,稍后。

当我阅读了很多关于为新语言实现垃圾回收的文章时,我才意识到,哦,我从未需要这样做。我意识到我从未需要这样做,因为我选择垃圾回收语言作为我的实现语言。

很好。

这是正确的选择。但这是在当时无意识地做出的明智选择。如果我回头看,这是一个非常明智的选择。

是否有任何 Go 功能使它变得特别……

困难,对吧?所以,我不会说,我不认为我能指出任何一个功能,我会说所有功能都是这样的。我必须编写三倍的代码才能用它来实现。

没有一个功能是那样的。我认为这是因为 Go 实际上非常适合应用程序,它是一种非常适合实现应用程序的语言,而 Elvish 显然是一个应用程序。你可以将其用作库,但我并不认为它是一个库。

我不太关心它是一个库。但是,我认为有一些事情让它有点痛苦,有点丑陋。在 Go 中,这又回到了,我不认为 Go 实际上是一个非常适合实现库的语言,因为我会提到两件事。

一是 Go 没有真正的枚举类型,也没有真正的标签联合。如果你正在考虑实现应用程序,你可能不太关心这一点。但是,如果你正在考虑实现库,那么 API 就可以用更干净的方式建模,如果你有真正的枚举类型或真正的标签联合。

例如,像事件处理程序一样,在这个行业中可能有 20 种不同的节点类型。如果你实际上有一个类似 Rust 的枚举类型,它可以列出这 20 种不同的节点类型,这将非常方便。这使代码更干净。

所以,即使我不认为 Elvish 本身是一个库,而是一个应用程序,但它是由一些更小的模块组成的。它们本身就像……对,对,在内部创建了一些小的约定,但它不会在整体应用程序级别上显示出来。另一个例子是 Go 没有关键字参数或默认参数。

人们通常的做法是创建一个结构体。当你想要使用参数时,只需将它们作为结构体的字段。如果你正在构建一个被很多人使用的库,这很好。

但是,如果你只是构建内部库或内部 API,它感觉就像有很多样板代码。所以,它并没有让 Go 的 API 变得最干净。

这是我的看法。所以很多时候,我只是盯着代码看。我想,我希望它看起来更干净一点。我希望它看起来更漂亮一点。但你必须接受语言的局限性。

所以,对于 Elvish,你还在添加一些功能,还是只是修复错误?它的状态是……是的。

我认为,短期内,在过去几个月里,我一直在开发 Elvish 的新框架。所以,如果你考虑一下我们之前讨论过的 shell 的真正含义,对吧?就像如果你考虑所有浏览器,有很多框架,例如 React、Angular 和 Vue,你可以使用它们来构建现代、干净的 Web 应用程序。

我认为有一些框架可以让你构建良好的、干净的终端应用程序。但我认为我们可以使用更多,特别是如果它是一个框架,你可以直接从 shell 中访问它。所以,如果你每天都在使用它,你可以使用这个框架来扩展 Elvish 本身,使其更具交互性。

我们可以使用它来实现新的终端应用程序,例如,可能在新的框架中实现它。所以,这已经进行了一段时间了,但我希望它很快就能合并到 Elvish 的主分支中。所以,当它发生时,Elvish 将会变得像……

嗯,我之前说过,关于 Elvish 的两件事是它是一种真正的编程语言,并且具有良好的 UI 功能。

我认为,如果你在 Elvish 中有一个框架,这两件事实际上会结合在一起,形成一个整体。这将成为一个非常易于编程的 UI。

我认为这会非常酷。所以,这就是接下来要做的。但是,如果你考虑一下长期未来,这又回到了我们之前讨论过的问题,很多人将 shell 与终端混淆。

我认为这有很多见解,它实际上只是你管理程序的一个地方,对吧?有很多事情你可以轻松地做到,因为这些是两个独立的程序。例如,如果我有一个 shell,它有命令历史记录,你可以查看你运行过的命令,但它不会告诉你命令的输出,因为 shell 实际上无法控制这些命令。一旦 shell 启动了这些命令,终端就会控制这些命令。所以,如果你能做得更好,让你的 shell 更紧密地集成在一起,甚至只是一个应用程序,那会很酷,但那有点过于未来主义了。

好的,我有点想,如果我能够在那里,并让它为你生成命令,那就太酷了。这需要一些时间。

是的,但我认为,通过新的框架,也许有人会将其作为插件添加到 Elvish 中,我敢肯定。老实说,像 LLM,它们可以用来……我用它们来生成一些 FFmpeg 命令,取得了一些成功。是的,FFmpeg 有很多不同的选项。它非常有用,但有时会给你一些非常混乱的 shell 命令。所以,我可能会信任 LLM 来生成一些命令,但我真的不会信任它……

来编写屏幕,特别是任何可能产生破坏性影响的屏幕,永远不要让它生成命令,风险太高。

大家好,我今天和一位新朋友一起,在 Timescale 的下午。所以,你能帮我理解一下 Timescale 是什么吗?

所以,Timescale 是一个 Postgres 公司。我们在云端和开源系统中构建工具,让开发人员能够用 Postgres 做更多的事情。所以,它用于诸如时间序列和最近的 AI 应用程序,例如 RAG 和代理。

好的。如果我们的听众想开始使用 Postgres、Timescale 和 AI 应用程序开发,你会告诉他们什么……

一个好的起点,如果你想开始开发 AI 应用程序,或者你对这个领域正在发生的创新感兴趣并想参与其中?好消息是,任何今天的开发人员都可以使用他们已经了解和喜爱的工具成为 AI 工程师。

所以,我们一直在做的 Timescale 与 Postgres AI 项目的工作,让开发人员可以使用他们已经了解的工具和数据库来构建 AI 应用程序。这正是 Postgres 的意义所在。

你可以承担新的、有趣的项目。你可以增加更多技能,而无需学习一整套新的技术。最棒的是,所有这些都是开源的,用于 Postgres AI 和 Postgres 向量缩放。

你可以使用 Docker 在本地机器上运行它,按照 Timescale 的教程之一,例如日志缓冲区,这些尖端应用程序,例如 RAG 和代理,而无需学习十种不同的新技术。只需使用你可能已经了解和熟悉的 Postgres 语言。是的,今天就开始吧。这是 Postgres AI 项目,只需访问 Timescale 的网站,找到 Postgres AI 或 Postgres 向量缩放教程之一,并按照教程开始成为 AI 工程师。

只需使用 Postgres,并使用 Postgres 来开始 AI 开发,构建 RAG 和 AI 代理,它是开源的。访问 timescale.com/flash-ai,使用 Postgres AI 和 Postgres 向量缩放,在本地桌面环境中进行操作,它是开源的,再次访问 timescale.com/flash-ai。

我实际上认为我们应该结束了。

好的,所以是的,我希望……但有一些流行的观点。

你还有其他观点吗?当然。所以,我的一个不流行的观点是,在测试方面,代码覆盖率应该是最低要求,而不是最终目标。

你想达到 100% 代码覆盖率吗?是的。

好的,好的。你能解释一下吗?我敢肯定很多人会解释一下。

对吧?对吧?所以,让我稍微……少一点激进,但不是通过改变我说的话,而是通过说,因为我们都想要 100% 代码覆盖率,但是如果你看看你的应用程序或库的测试覆盖率,它可能不是 100%。

但是我们都认为,我们所有的代码至少都必须出错过一次,以便我们知道它实际上是有效的。如果你的代码中有 10 行代码从未出错过,那为什么还要保留它?为什么要保留它?你的答案可能是,我实际上已经进行了单元测试,并且依赖于这些细微的代码行,但我的自动测试没有捕获到。

我会说,从精神上讲,这算作 100% 代码覆盖率。所以,我们都在努力做到这一点,只是我们无法在自动测试中实现 100% 代码覆盖率,在单元测试中,但我们一直在努力。我们都知道,如果你写了 10 行代码,你必须运行它一次,以确保它实际上是有用的。

我之所以说这是最低要求,是因为运行代码,确保代码至少运行过一次,这仅仅是一个开始,对吧?因为如果你编写了一个程序,它只是打印 a 和 b,但它也打印 a、m 和 b,但实际上打印 c,你运行过它吗?你运行过它一次,但你没有检查,但你从未检查过输出是什么。

该代码在技术上具有 100% 代码覆盖率。但是你仍然不确定它是否正确。为了确保它正确,你实际上需要进行正确的检查。

所以,运行代码一次是一个开始。你还必须确保你实际上检查了代码的效果。这是我对代码覆盖率的看法。我认为人们不愿追求 100% 代码覆盖率的原因是,首先,我们并没有真正充分地将我们所做的一切都记录在代码覆盖率中。

如果你编写了一个程序并进行单元测试,你在单元测试中触发的代码并不计入代码覆盖率,并且有时如果你进行集成测试,工具也会阻止你将其计入代码覆盖率。大多数指标实际上只计算单元测试。但我认为,Go 在几年前实际上已经实现了对集成测试代码覆盖率的支持。

所以,你现在可以为集成测试生成代码覆盖率,我认为这将使许多项目的代码覆盖率大幅提高,因为当你运行整个二进制文件而不是在单元测试中运行单个函数时,通常会执行许多代码路径。所以,我认为我们都应该接受这一点。

另一个原因是许多库根本无法测试,对吧?例如,如果我的程序只是……如果我的函数只是导致 OS 停止退出,你不会在单元测试中添加测试,因为它会退出整个程序。但为什么不呢?你应该能够测试这些东西。

如果你的程序发送数据到网络,这有点难以模拟。我们通常的做法是提取程序的计算部分,只测试这些部分,而忽略我们与真实世界交互的部分。但我不是在责备任何人没有这样做。我的意思是,生态系统并没有给我们足够的工具来轻松地进行这些测试,但我认为这应该改变。

好的。我的意思是,不是一个极端的、激进的观点,现在我认为,我可以保留一些理性,你知道,这是正确的。

好的,是的,我可能……

不是一个流行的观点,但是,就像我最近在录制这段内容的几天前,我看了一个关键演讲,那是一个关于Tob宇宙大会的会议,有一个关于Copilot的演讲。你知道,它能够……像整个Spark系统一样,无论它有什么能力,从头生成应用程序,等等,这通常是卖点,市场上的东西,说你可以从头构建一个应用程序,哇。

而且,它总是……宣传总是生成这些新的、一次性的,通常非常小,功能非常少的应用程序,只是展示一下,嘿,看看这个东西,你甚至不需要程序了……但是这些东西通常是非常简单的,你知道,一些应用程序。并不是说其中一些是软性的,但是当你从头生成东西时,就像,你知道,一个绿地项目,对吧,你基本上可以从头开始编写所有东西,用于现有的系统,用于已经运行多年的现有大型系统,你知道,甚至几十年,对吧?这些是我需要帮助的。

这些是我需要帮助的事情,你知道,当我被要求维护一个系统时,比如,我被一家公司雇佣,他们已经运行了他们的软件,运行了他们的业务,已经超过十年了。而我需要做出改变,或者需要一个功能,无论如何,在这些系统中,我需要帮助,对吧?我不是在生成,你知道,云应用程序或要做的应用程序,你知道,从头开始用灯泡。

我试图融入现有的代码库。所以他们展示过的一些东西,我刚刚看到过,它适用于Java,但是有一些东西可以帮助你升级,或者类似于你的应用程序,也许你有一个老式的,你知道,Java应用程序,也许你需要升级,你知道,使用的机器版本,或者无论情况如何。但是,它会遍历项目中的所有不同部分,基本上改变圣塔,也许你使用旧的圣塔X,新的圣塔代表某些时间,你想要使用Lambda,无论它是什么,对吧。

它会遍历,允许你更改所有这些不同的文件,升级现有的应用程序,对吧?但我认为这还不够。我认为总会有一个需要人提供一些额外联系的需求,对吧?一些关于LM的背景故事。

这需要改变,因为我在我的时间里看到过很多技术,很多人没有去工作,没有去工作在组织上。他们带着自己头脑中的孤立知识走出大门。你知道,它没有记录下来。你看到一段代码,你可能看到这种情况,你改变了一些东西,你认为你正在做正确的事情,然后你注意到你打破了别的地方的其他东西,没有意识到,哦,哇,我不知道为什么,只是做错了事,我只是想,然后你最终打破了你不了解的依赖或依赖代码以特定方式工作的东西,对吧,所以这种技术会随着时间的推移而累积。

而且,你知道,人们并没有跟踪,你知道,他们改变或不应该改变某些东西的原因,对吧?所以当你把所有这些情况都扔到一起时,并不是说有一天我们不会能够拥有足够强大的模型来解决这些问题,但是这些是,当他们进行这些营销演示并让每个人都兴奋于你丢失的语言模型时,我想要看到的演示。一个具有许多假设的现有应用程序,受害者,就像受体一样,因为那是我的工作,我不是在生成,你知道,3D国际象棋程序,现在工作,这不是我正在做的事情,我如何知道我的生活是什么。

我处理的是老式的、遗留的系统,有很多假设,以及在我接触到这个代码库之前就做出的许多决定。就像处理一堆混乱的媒体一样,对吧?不是琐碎的东西。

所以,也许不是一个流行的观点,但更像是,我想,你知道,一个请求,是什么,更像是,让每个人都知道,是的,演示很酷。但是我们仍然需要一些时间才能真正利用语言模型来做我们真正关心的事情,对吧?除非你正在构建一些琐碎的东西,在绿地项目中,这并不是我们大多数人。所以讽刺的是。

我认为,这里100%的测试覆盖率会有帮助。如果你,如果你的代码有非常好的测试覆盖率,你可能会更有信心让语言模型尝试对它进行更改,因为你有一些信心,一些……

旧的行为可能与代码测试时做出的许多假设一起保留下来,对吧?

我认为语言模型也可能对建议你……帮助你实现更高的测试覆盖率很有用,建议你……它们擅长生成东西,所以你可以生成测试。

这当然是我越来越依赖模型来做的事情。你知道,如果它是什么,我很好奇TDD实践是如何处理这个问题的,对吧?因为如果我可以专注于让代码工作,然后我可以抛出一个敌人,说,嘿,对这个进行测试,那么我可以专注于,你知道,获得概念证明或可工作的代码。

我可以专注于完成它,因为我认为很多人已经遇到过编写测试的乐趣,因为它帮助他们思考,对吧?但是我们喜欢先写代码,然后测试。所以也许,你知道,那些想……我想,你也可以让语言模型先生成你的测试。

但是你必须非常详细地描述你的提示,告诉它在编写代码之前,它应该生成通过测试的代码。但是,是的,这是一个有趣的,一个有趣的开发人员时代。我会告诉你。

是的,绝对。

哦,好的。很高兴有你。很高兴能够真正了解……

Elvish,他在如何使用Go来帮助他完成这个旅程。是的,我希望项目能够继续成功。感谢你的参与。

我很高兴能来这里。谢谢。

感谢大家收听Go时间。我们感谢你们每周都花时间和我们在一起。如果你还没有,请查看ChangeLog新闻。

这是该行业唯一的周报。它也是一个播客。一位读者称它非常好。他认为它是一个竞争优势。在changelog.com上阅读和收听最新一期,特别新闻。

再次感谢我们的合作伙伴Fly.io,以及我们的赞助商Century。你可能想使用代码changelog来节省你的团队计划。现在就到这里,但我们下周的Go时间再见。