permlink
我觉得如果换个角度,其实是 RPM/dkpg 由于政策或技术原因满足不了” 现代 “的开发方式。
golang/nodejs/rust 那样一个功能就 import 一个新的依赖,其实蛮符合 *nix 讲究的 "Do One Thing and Do It Well”。
C++/C 经常需要一个包罗万象的依赖 Glib, Qt, Boost, POCO, icu 或者像 C# 直接需要微软写的巨大无比的 .NET 框架。其中的一个原因就是他们管理依赖很麻烦。
就像许多 C++/C 写的程序,目录里面经常有个 Util 放一些常见的东西,比如说 fcitx-utils,systemd/basic,git/string-list。
如果 C++/C 管理依赖方便一些,那些东西就会变成很小的工具库,然后被许多项目依赖,共同维护。对于 God 级别还有靠写开源软件吃饭的人,写那些东西当然不难,但是如果从社区整体来看,那些重复的劳动似乎没有什么必要。
因为 C++/C 自带的处理字符串的功能不怎么好用,所以许多项目都有一个很相似的 String_util.c
不断地重复实现着几乎一模一样的功能 =。=
而对比起来,那些新的语言想要更多操作字符串的函数 -> import string.go
,要另一种风格的函数式编程 -> import lodash.js
,要解压 zip -> import zip
,要拯救世界 import world-saver
!
从库的维护的角度来说,以开发 Glib 的人为例,他们需要维护处理字符串,时间,线程,数据结构,regex,配置文件等等一大堆东西。
对比起来,新语言单独模块的维护者只需要维护可能连一张纸不到的函数列表。我只负责一个很专门的领域,由于维护成本不高,我可以把 API 设计的特别精美,而且可以及时地确保能在新版本的操作系统/编译器上使用。
另外对于开发者来说不想要 Glib 里面处理线程的部分,为什么要添加一个给巨大无比的依赖。
依赖一个 Glib 然后让 GNOME 的开发者决定你能用什么 VS 亲自挑选各个模块的组件来组装一个对应专门程序的 Util-lib,按需添加, 且每一个模块都是小而精致
另外从开发方式来看, 那些新语言也更符合开源的精神。
我用一个库,但是原作者开发的太慢,或者不喜欢原作者的方向,或者需要添加一些只有我一个人需要的增强功能。只要 fork 一下,然后 import myrepo/string.go
。
如果我制作了这个库的变种,可以帮助另一堆用户群,凭什么只有原作者的那个在发行版的仓库里面。
所以我认为是 RPM/Dkpg 落后于那些现代语言自带的包管理器,让他们去适应 rpm/dkpg 应该是在开倒车。
能跨平台现在都是现代语言的基本功能了,这一条就让他们有足够的理由写自己的包管理器。
操作系统的包管理在面对这些语言的时候,应该直接放弃处理依赖,直接交给编程语言自己解决。
并且标准化每个语言单独使用的目录(类似于 MacOS 的那个 /library
& ~/Library
,标准化一个 /usr/library/python
, /usr/library/node
, /usr/library/latex
)
而且从云端 Maven/Crates/NPM/Pypi/Melpa/OPAM/Hackage/Quicklisp/CPAN/CTAN 直接拉取依赖本来就解决了一部分依赖处理麻烦的问题,
不如干脆让操作系统的包管理,直接通过 pip/cargo/opam 之类的东西直接和各个语言的仓库连通,并且自动地在本地注册 python::xxxx, go::yyyy, rust::zzz 的包。编程语言的包管理同时还会注册一下依赖的包。
如果一个软件依赖 Rust::string_util,用操作系统的包管理器安装的时候,直接从 Crates.io 查询并安装,直接省略 -> 打包-> 弄进操作系统的仓库的流程。
至少 python/go/rust/ocaml/java… 的包管理器本来就不依赖具体的发行版,这样顺便还可以进一步减少不同发行版之间没有必要的区别。
这个脑洞应该不会在 RPM/dkpg 上实现,或许可以 Hack 一下操作系统的包管理器 (arch/void/),从编程语言的仓库安装以后,通知他们这个包有了!这样操作系统的包管理器就变成了 Meta-package manager(大雾)。