Impressions of the programming languages I have come across
C ¶
老登语言,不如说我非常难以理解为什么这玩意几乎就是个汇编的封装器却会成为各大高校的入门教材……?
C 语言根本谈不上什么设计,因为它可以说就是没有设计。它就是个被造出来的人类可读的汇编简记法,然后碰巧火了而已。所以我不承认什么 C 的优秀,C 优秀根本就不在于它是 C,而在于用 C 写的那些 killer app,比如 UNIX。
的确,实事求是的说,C 语言是最通用、最可移植的语言,但是这并不是因为 C 的设计优秀,恰恰相反其实是因为 C 就是一个汇编的封装,可移植的其实不是“优秀的语法”,而是汇编。如果你要说 C 优秀,那么汇编也优秀。
C++ ¶
Awesome language only when for algorithm competitions. 内存泄露?让它漏呗我们要的是超快速度!
但是除了算法竞赛以外 C++ 实在是拉胯得让人无话可说。
其实 C++ 挺遗憾的,C++0x 几乎就是个废物,在 90 年代末相对高级的语言群魔乱舞起来的时候只有 C++ 在那里不思进取吃老本,终于等 C++11 出来了挺大江山已经流失了,笑
你很难找到一门语言又进步又过时,既简单又难……除了 C++
可以说 C++ 其实很 fancy,它融入了很多新概念,但是它融入得不够好。都怪那该死的兼容性(乐)。到现在 C++ 的语法可以称得上一句沉重,但是沉重中又没有多少用得上的……
举个例子吧 std::optional<T>
. 这玩意好吗?太好了。Null 的发明者都亲口说 null 完全是一次数十亿美元级的错误了。而我得说 C++ 简直就是这场错误的集大成者。C++ 里到处都是类似 Null 的东西。拿特殊的值标记无效的内存区域。灾难中的灾难。
为此明明有 Rust 可以学的,结果 C++ 就学了一个皮毛——把 optional<T>
抄过来了,然后就不管了。std::map
都有好多不同的取值方式了,什么 []
在 key 不存在的时候返回默认 constructor,什么 at
抛出 std::out_of_range
异常, find
返回 end
迭代器,妈呀结果就是没有用上 STL 自己的 optional<T>
的——excuse me?所以你弄一个 optional<T>
是为了走时髦吗?
至于 C++ 很难的问题,其实倒是可以接受,因为 C++ 简单——你可以同时觉得 C++ 简单和难得要死。听上去很离谱,但是是 C++,倒也正常.png
C++ 就是那种非常适合算法竞赛、非常适合学生上课的语言。反正内存安全去他爹,裸指针乱飞,嵌套结构稍微学学就会,性能还超级高,还封装了一堆常用数据结构,它不火谁火.png
但是复杂度摆在那里。如果要简单,那就一定有复杂在反面等着你。一门面向底层的语言,讲究极致压榨性能极致压榨空间甚至到处都是直接操作内存、还有超级多历史包袱的语言居然还能做到让一个初学编程的人轻松写出一坨狗屎也能通过编译,那要写出“优雅”的代码它肯定会很难,这一点也不难理解吧。就是它有点难过头了而已。
很难说如果 C++再这样赶时髦地加入新功能下去是不是某种自取灭亡,只能说希望不是吧
以下是说怪话时间
C++ 真是一门优美又简洁的语言啊 真是优优又美美啊
你看别的语言还在 <T extends U | V>
真是难难又懂懂啊 extends 是什么?竖线又是什么?真是不说人话让人难以理解呢
再看我们 C++ 啊跟念诗一样
template<typename T>
requires (std::is_base_of_v(T, U) or std::is_base_of_v(T, V))
bar foo(T&& xyz);
你看我写了一个 T 啊当然是模板啊 又 requires 啊 is base of 就是基类啊 用 or 取代竖线英英又语语啊 哪怕是不懂 C++ 的人看到也能马 ↑ 上 ↓ 理解这是在说什么呢
真是容易理解呢呢呢这就是我们 C++ 啊真是 CC 又加加啊
Crystal ¶
截止到本文的上次编辑 Crystal 还是一门非常初生(?)的语言,嗯就是语法描述也不全,生态少得可怜,连编辑器提示都没有,各种地方都透露着种种不成熟
但这玩意确实是个静态、native 语言,还是 native 语言中的异类,在 native 中做到了可以尽可能地少写类型,而且居然还有类 Ruby 语法,弄得这语言跟个动态语言似的
非常喜欢的是 Crystal 继承自 Ruby 的一个特性,你可以随意 Reopen 一个 class 然后添加进去自己的方法。甚至官方库都是这样做的,比如 JSON 库:它直接把 int 打开了然后往里面塞了一个 to_json
的成员函数,然后所有 int 就能 to_json 了,非常的好用
struct Int
def to_json(json : JSON::Builder) : Nil
json.number(self)
end
def to_json_object_key : String
to_s
end
end
这个真的很妙,虽然它有一个众所周知的缺点,要克制,小心和别的库添加了冲突的成员函数。但是相比之下这个带来的简直是致命的诱惑,可以通过 reopen 创造非常具有表达力的语法,就像 Rails 做的那样。
Elixir ¶
TODO
Gleam ¶
灵感来源于 Rust 的函数式语言。虽然但是,感觉有点……简陋……
在 tour 里一开始被它的长相骗了,直到看到 Gleam is an immutable language, so using the record update syntax does not mutate or otherwise change the original record 才惊觉欸 www 怎么是 immutable 的
暂且还非常不成熟,所以呈观望态度吧。
Gleam 语言初见文章见:仿生电子锈会梦到自己变成纯函数式吗:gleam 语言初见
Haskell ¶
纯函数式编程教科书级别语言。
Haskell 的语法写起来和不是函数式的语言真的完全不一样!在 haskell 里面 a = x
完全不是赋值啊是定义了一个函数 a
返回 x
xd
Javascript / Typescript ¶
JavaScript 系的缺点太多我觉得没必要说了,毕竟一个 10 天写出来的语言,蹭 Java 的热度,然后意外成了互联网标准这种事情……只能说这就是现实()
简单说些不好的地方:
- 没法重载运算符,写带计算的代码很丑陋(不过作为为网页服务的语言,遇到大量计算就上 WASM 了
- 你永远不知道是坨什么的
this
- 到现在都没扯皮出公认的标准,都是 ES6 和 CommonJS 分立
==
JavaScript 系最大的优点大概是非常原生非常友好的 async/await。单线程的设计又让你根本不用在乎资源加锁之类的异步噩梦。
而且 JavaScript 可能可以说是流行语言里最把 lambda 函数发扬光大的语言。也是动态类型语言里类型标记做的最好的——它甚至演化出了专门的 typescript
也是因此,TypeScript 的类型系统实在是过于完备了,以至于大家仍然某种习惯于 JS 写法然后通过复杂的体操来保证 JS 写法是类型安全的,这其实有点呃呃
并且 TypeScript 其实不完全是 null safe 的,比如说
const a: string[] = [];
a[1]; // ts says string, but actually undefined
typescript 不检查 out of range 的可能性,以至于当你给出 string[]
这样的类型的时候 ts 会认为任何一个 number
作为 index 传入都能得到一个 string
—— 但致命的是,实际上翻译出的 js 它可能是 undefined
这个非常的坑,可以说是 TypeScript 上巨大的一个洞,直接导致 undefined 可以打穿 ts 的类型系统。可惜木已成舟便是了。
Julia ¶
TODO
Lean 4 ¶
TODO
Lua ¶
TODO
Python ¶
Python 的火爆完全来自于优秀的库而不是优秀的语言设计,夸张点说 Python 完全展示了什么叫糟糕的语言设计硬被优秀的库带飞。
完全不觉得 Python 是很适合工程实践的语言。它更多的是一门教科书语言,类似以前的 BASIC,很适合初学者浅尝辄止地入门,但是稍微复杂一点就完全是噩梦,更何况 Python 的语言设计还很糟糕
我不喜欢 Python 设计者的审美,比如认为 lambda 函数没有必要可以用具名函数取代。也不喜欢缩进表示一个块。
并且 Python 真的不思进取。换其它语言如果有这么多优秀的库这么多资金绝对不会放任自己的性能烂成这样,但是 Python 到写下这一段的时候都还没有成熟的 JIT。
Ruby ¶
相当优雅的设计,动态到叛逆。我对 Ruby 最大的好感来自它不试图用各种什么哲学什么风格指南约束程序员,如果 Ruby 有哲学,那就是最大的让程序员幸福
因此写 Ruby 代码确实很舒服——如果抛开过于动态导致的特别容易出运行时类型错误不谈的话……
Ruby 最使我影响深刻的或许是类似这样的代码
3.times do
say "hello!"
sleep 1.seconds
end
这是我在其他任何语言都没感受到的神奇的 magic,或者说其它语言当然也能写出类似的代码,但只有 Ruby 鼓励这样做,并且到处都是这样做,让整个代码特别具有“阅读感”,哪怕一个什么都不知道的新人看到这样的代码估计也能瞬间理解它在干什么,只是可能不知道具体是什么原理而已。
另一个例子仅用了些许代码实现,却几乎引发了惊愕的程度。
Array#second
到#fifth
(以及挑衅意味的#forty_two
)。这些别名的存取器,非常严重地冒犯了常发表意见的支持者,他们说:这简直太过度设计了(几乎是编程时代的结束),这些写成Array#[1]
、Array#[2]
(以及Array[41]
)不就可以了嘛。但时至今日,主要的抉择还是,让我自己开心。我喜欢在终端或测试里编写
people.third
。不,这不合理,也不高效。可能我有病吧,但这仍能让我发自内心地微笑,满足了这个理念,也丰富了我的人生,帮我在过了 12 年之后,还仍继续参与 Rails。
这个真的非常叛逆,在许多语言都在宣传“做事情只有一个最好的方式”(盯 Python),甚至直接在程序语言上拒绝和故意劣化某些风格的时候,Ruby 的想法缺是做事情可以有很多种方式,程序员来选择最让自己幸福的方式。大家都在把程序员当成 思路 -> 代码
的翻译工具,而 Ruby 选择将程序员视为作家。Ruby 的放纵让人感到某种幸福。
另外一个令我有些感动的是 Ruby 到现在已经逐渐不再流行,但是仍然在努力做出改善,Ruby YJIT 已经可以进入生产环境并且被 Rails 默认开启。以前 Ruby 最大的诟病是它慢,慢地出奇,比 Python 还慢,但是现在 Ruby 的速度已经逐渐追上的 Python。
说完优点,Ruby 一个很大的缺点是:你家语言三个闭包.png
在 Ruby 中有三种不同方式去声明一个闭包,我觉得这完全是没必要的,
Rust ¶
虽然 Rust 社群的名声不好 语言原神,但是不得不承认的是 Rust 是一门非常优秀的语言。我对 Rust 最大的好感度其实不是内存安全性 —— 毕竟合格的程序员应该能用任何语言写出内存安全的代码 —— 而是 Rust 非常优秀的模式匹配和语法。
Option<T>
是现代且优秀的思路,我觉得所有可能带 null 的语言都应该引入它。而 Rust 在这一类功能上做得非常好。虽然 Result<T, E>
和异常的优劣可能有待商榷。但是大部分情况,Rust 的 enum 的设计做得非常的不错,完全改变了 enum 曾经只是个不蕴含实际信息的“标记”的情况。
如果 rust 没有内存安全作为梗小鬼的吹嘘资本,哪怕是优秀的语法设计也能让它成为一门优秀的语言。
Zig ¶
better c,相比 C 而言有出色得多的设计。
比较喜欢 zig 似乎是从 go 上继承来的思路: defer
关键字,保证在各种情况下退出当前 scope 的时候执行,这意味着相比于 C,Zig 能更简单的确保内存分配总是伴随着释放,更不容易写出内存问题
比如经常可能看到这样的 zig 代码:
pub fn create(
alloc: Allocator,
) CreateError!*App {
var app = try alloc.create(App);
errdefer alloc.destroy(app);
// ...
}
defer 比傻傻的手动在函数最后释放,一不小心还可能忘掉可高级太多了!
不那么喜欢的是 zig 不支持方便的闭包,或者说,虽然可以写出闭包来但是很麻烦。
然后 zig 的 compiletime 非常的神奇,我很喜欢这个,编译期间执行任意函数,把编译期干的活和普通代码融合在一起,简直太好用了
专门为 zig 写了一篇 intro,见:更好的 C 语言:Zig 初体验