语法简单到只用8个符号,学它却像在拆解自己的脑子,到底卡在哪一步?
这篇东西不是教你怎么学编程的。我就是最近翻了些老资料,发现很多人说的“最难编程语言”,其实根本不是一回事。有人说C++难,有人说Haskell绕,还有人吹嘘Malbolge——但这些“难”根本不是同一回事。我查了原始文档、作者访谈、论坛几十年的吐槽帖,还试写了几个小例子,才搞明白:有些语言难在不讲理,有些难在太讲理;有些是故意让你写不出来,有些是你写出来后自己都不敢信。
Haskell刚上手那会儿,我照着教程打一行`main = putStrLn "Hello"`,结果报错说类型不对。不是拼错字,是没加`IO`。后来才知道,它根本不允许“做事情”,只允许“描述事情”。你想改个变量?不行。你想循环十次?得用递归+模式匹配,还得想清楚每次调用会不会爆栈。最烦的是调试——没有print,只能靠类型推导猜哪一步断了。编译器不告诉你“错了”,它说“你写的这个根本不是我理解的语言”。这不是学语法,是逼你用数学证明题的思路去写记事本。
Prolog更离谱。我写了个“找祖父母”的规则,运行后啥也没输出。不是代码错了,是它算出来“无解”。可我明明定义了爷爷和爸爸的关系。查了半天才懂,它不是执行顺序,是反向匹配。变量不是容器,是未知数,整个程序像在解方程。你不能说“先查爸再查爷”,得说“如果X是Y的爸,Y是Z的爸,那X就是Z的爷爷”。写完还得祈祷回溯引擎别剪掉正确路径——它不报错,只是默默给你一个空结果。

Lisp(我用的是Racket)头三天全耗在括号上。不是数不清,是根本不知道哪段是数据、哪段是代码。`'(+)`是列表,`(+ 1 2)`是计算,可它们长得一模一样。我试着写个宏,想把`defn`变成自己的函数定义,结果宏展开后直接语法报错——不是我写错,是它在编译时就把代码当数据改了一遍,我连改的是哪一行都找不到。后来才明白,它没有“代码”和“数据”的墙,只有“表达式”这一个东西。你得习惯所有东西都是树,连`if`都是节点,不是关键字。
Malbolge我只跑了它的Hello World,不是我写的,是网上抄的。那串字符像乱码:`('&%:9]!~}|z2Vxwv-,POqponl$Hjig%fdcba`。它每执行一条指令,下一条指令地址就自动加密;内存是三进制的;同一个字母,在不同位置干的事完全不同。1998年作者说“这语言不该被人写出来”,结果真没人写出第一个程序,直到两年后有人用暴力搜索加数学建模才凑出来。它不是难学,是难“确认自己写对了没”——因为运行结果和你写的看起来毫无关系。
INTERCAL更绝。它要求你写`PLEASE`才能执行语句,少写一句报“IMPOLITE”,多写一句报“OVERLY POLITE”。`COME FROM`不是跳转,是等别的地方跳到你这来;`?`运算符是把两个数的二进制位交叉拼一起;`~`是按位挑选。它1972年出生,专为嘲讽当时那些“正经语言”而生。你写不出功能,但能写出态度:用最客气的话,干最不讲理的事。它的难不在技术,而在你得一边写代码,一边演个彬彬有礼的疯子。

网上总有人说C++最难。我试了,它确实坑多:指针忘删、智能指针用错、模板报错信息三屏长……但它每一步都有解法。你搜“C++ shared_ptr leak”,能立刻找到答案;你问“怎么避免double free”,有一百种文档告诉你。它的难是路太多,不是没路。而Haskell的monad、Prolog的回溯、Malbolge的自修改,是路本身都反着长。Java也常被拉进来,但它把最难的内存和线程藏在JVM底下,你最多被Spring配置整懵,不是被Java语法逼疯。
有人说“学难语言锻炼脑子”,我觉得扯。我练了两周Haskell,没变聪明,只是不再随便写`for`循环了。看到一段Python里嵌套五层`if`,现在第一反应是“这能用类型系统提前拦住吗”。Malbolge我没学会,但看到Rust的`borrow checker`报错,第一次没骂编译器,而是想:“哦,它真在帮我防那个我还没写出的bug。”
这些语言像不同的镜子。Haskell照出你对“变化”有多依赖;Prolog照出你习惯了“命令”却忘了“定义”;Lisp照出你把代码当动作,而不是结构;Malbolge照出人类对确定性的执念有多深;INTERCAL照出我们写代码时,其实一直活在各种隐性礼貌规则里。

它们不是用来日常干活的。没人用Malbolge写支付系统,也没人用Prolog写安卓App。但你要是真写过几行,再看自己常用的Python或JavaScript,会突然愣一下:为什么这里能随便改变量?为什么那个循环一定要从0开始?为什么错误非得等运行才出现?