我理想中的编程语言

2025/8/103953 字

引言

在了解并学习的多种编程语言之后,我对自己喜欢哪些编程语言的方面讨厌哪些语言的特点,有了一个大概的了解,也有了自己的一些看法,这篇文章就整理一下自己对各种编程语言的特点的看法,阐释和论证我理想中的编程语言应该是什么样子的。

首先我认为一门理想的编程语言,它的目的应该是为了高效的开发解决问题,也就是好用,同时还足够严谨,让使用它的人不容易出错,同时还有一致和优雅的设计哲学。

下面我将从一门编程语言的方方面面来介绍我理想中的编程语言应该具有什么特质,而不应该具有什么特质。


一、类型系统:强静态类型与“万物皆引用”

强静态类型 + 类型推导

首先语言一定要是强类型系统,可以带有类型推导。

你可能会质疑说动态的类型系统才更好用更好开发。但我认为这仅限于一个人,并且是开发小型脚本的时候才好用。

在开发大型项目以及多人协作的时候,如果没有强类型的约束将会出现各种未知的bug。

本质上是因为类型性也是一种注释,通过一个函数的输入的参数的类型和输出参数的类型,就可以大约知道该如何使用这个函数以及这个函数它的作用是什么。这是一种非常强大的能力。

总之加上类型系统并不会在开发的时候让写代码的速度变慢多少,但绝对会减少代码的歧义性以及出错的概率。

• 正面案例:Rust的类型推导能力

• 反面教材:Python类型Hint的事后补救(虽然可以靠静态检查发现一些类型上的错误,但实际上运行的时候不会强制你一定要解决,所以仍然会导致错误的发生,只不过对于那些有意识的人可以避免)


所有变量都是对象引用

我之前在关于rust生命周期的文章里面提到过:所有变量都只是指的内存中的一块数据,并且全部都是在栈上的(除了全局和静态变量以外,其实本来也不应该用全局变量)。

我理想中的编程语言为了一致性,我认为应该把所有类型的变量包括基本类型都只在栈上储存一个指向堆上真正数据的引用。同时通过引用计数的方式管理内存。

这就和Python目前采用的方式比较类似,可以知道这种方式非常易于编写代码,没有rust在编写时的考虑多种变量类型(在堆上和栈上,引用和所有权)的心智负担。

不过和Python不同的是这门语言没有GC。

你可能会说那如果遇到循环引用该怎么办呢?

我设想的解决方法是引用计数只追踪栈上变量的引用,而堆对象的回收则依赖于栈变量的生命周期。这样可以避免循环引用的问题。因为所有变量都是在栈上,而在栈上的变量随着函数的调用和返回有自己的生命周期,当一个在堆上数据的栈上引用清零的时候也就意味着这个数据不再被利用,反之亦然。同时,通过严格限制在栈上变量的引用计数也规避了堆上数据互相引用的循环引用问题。

目前在我看来就是完美的践行了RAII思想的方法,而不是像rust那样通过严格控制引用的生命周期进行妥协。

• 内存模型:栈变量生命周期决定堆对象回收(对比Rust的Box负担)

• 实现原理:堆数据随栈变量销毁自动释放

• 正面案例:我还没有看到相关这样实现的语言

• 反面教材:Python 用GC来解决循环引用,没有彻底解决问题,是一种妥协;Rust 增加引用的生命周期等概念为严格的所有权机制打补丁,是一种弯路。


二、语法设计:从表达式到函数式-OOP融合

表达式与语句彻底分离

首先,当今的任何语言的代码都是由语句组成的,而语句又由表达式组成。简单来说语句就是代码中写成一行可以直接被执行的,但是不能产生值参与表达式计算的代码。而表达式则是可以产生值的代码。

一门理想的编程语言应该在语法上清晰的可以分辨出哪些是语句,而哪些是表达式这两者有本质的区别如果你在语法上就对两者没有进行区别那么使用者在使用的时候就只能对特定的语法进行记忆,而不是总结出一套本质的规律,增加了开发者的记忆负担,不易用。

比如下面的Python的例子:

python
a = 1 # 这是一个语句,没有结果值,可以直接执行
a := 1 # 这是一个表达式,有结果值(1),不能直接执行

可以看到第一行的赋值语句,从表现上可以发现它是一个语句,不过它和下面的表达式没有语法上的本质差别,使得不了解与语句和表达式区别的人难以区分两者,即使了解两者差异也必须要通过实验才能知道谁是表达式谁是语句。

而rust这一点就做得非常漂亮,你就能发现带分号的就是语句而没有分号的就是表达式。

rust
let mut a = 1; // 这是一个语句,
println!("a: {}", a); // a: 1
let b = (a = 2); // "a = 2" 是一个表达式,返回值是 ()
println!("a: {}, b: {:?}", a, b); // a: 2, b: ()

上面的例子还不足以显示这种优越性,在看下面两个经常会使用和遇到的例子

在rust里面循环语句条件判断语句以及单纯的大括号,这种末尾没有带分号的,按照一致的设计,没错,他们也是表达式,是可以有结果值的。

rust
println!("{}", if true { "Hello, world!" } else { "Goodbye, world!" });

let mut sum = 0;
let mut i = 1;
println!("{}", 
    loop {
        if i > 100 {
            break sum; // 将 sum 作为 整个 loop 表达式的结果值
        }
        sum += i;
        i += 1;
    }
);

println!("{}", {
    let w = 10;
    let b = 5;
    w * sum + b // 以最后一行的表达式的值作为整个表达式块的结果值
})

结果如下:

plaintext
Hello, world!
5050
50505

这样对于任何的rust的代码只需要看待末尾有无分号,就知道它到底是语句还是表达式,对于那些末尾没有分号的代码,作为表达式就可以知道他有很多种神奇的用法,就像上面那样。

这是不需要靠文档,在知道了语句和表达式的定义和区别之后,凭直觉就能知道的事情。

• 正反面教材:上面说了

• 理想语法:let result = { 逻辑; 返回值 }(无需return关键字)。或者有其他可以明确的区分表达式和语句的方式也行


函数式为骨,OOP为皮

我们知道传统面向过程变成实际上就是把数据通过指令进行操作。

数据的更高层次的抽象已经有了,那就是面向对象编程(OOP),它通过类和对象的方式将数据和操作数据的函数进行封装。

不过长久以来,我们执行操作的方式仍然还是用的面向过程的指令及一行行地编写如何处理数据的代码,达到我们想要的效果。

不过现在我认为面向过程的一行行操作数据的方法已经过时,更有前景的是声明式的编程方法。

声明式的编程方法,顾名思义就是通过声明的方式来描述数据的处理过程,而不是一行行地指令式地告诉计算机如何处理数据。

我用筛选表格中的数据的过程为例:

假如说我们现在需要筛选出一个表格中所有年龄大于18岁的人,并且将他们的名字转换成大写。
在传统的面向过程编程中,我们可能会写出如下代码:

python
people = [  
    {"name": "Alice", "age": 20},
    {"name": "Bob", "age": 17},
    {"name": "Charlie", "age": 22}
]
result = []
for person in people:
    if person["age"] > 18:
        result.append(person["name"].upper())
      
print(result)  # 输出: ['ALICE', 'CHARLIE']

但是对于这种表格的查询,现在更常用的是 SQL 语句,过程如下:

sql
SELECT UPPER(name) 
FROM people 
WHERE age > 18;

这就是典型的声明式编程方法,我们只需要告诉计算机我们想要什么样的结果,而不是一步步地告诉它如何处理数据。

这种方法的好处是代码更简洁、更易读,同时也更容易维护。

因为这是一种更高层次的抽象,只需要描述各个流程的步骤,而不需要管各个流程,具体是如何实现的。

函数式编程也是一种声明式编程方法,它通过函数的组合和链式调用来处理数据。

同样的数据库查询,我们可以用函数式编程的方式来实现:

python
people = [  
    {"name": "Alice", "age": 20},
    {"name": "Bob", "age": 17},
    {"name": "Charlie", "age": 22}
]
result = (
    map(lambda person: person["name"].upper(),
        filter(lambda person: person["age"] > 18, people))
)
print(list(result))  # 输出: ['ALICE', 'CHARLIE']

非常优雅, 不过Python原生提供的这种嵌套的调用方式就是💩(因为你去看源码就会发现Python竟然是把 map和 filter这两个动作用类来实现的,我的天哪,滥用OOP的典型),并且 lambda 表达式只能放一行语句,也是💩。

还是 Rust的函数式编程更优雅和好用:

输出 1 到 100 的质数数组

rust
println!("{:?}", 
    (1..=100)
        .filter(|&x| {
            if x == 1 {return false;}
            for i in 2..=((x as f64).sqrt() as u32) {
                if x % i == 0 {return false;}
            }
            true
        })
        .collect::<Vec<u32>>()
);
// 输出: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

总之,我认为应该把 OOP 仅用于构建类型(Struct + Trait),用函数式描述过程。两者结合是对传统面向过程的更高抽象。

• 痛点批判:Python的map/filter返回迭代器对象(违背函数式纯值理念)

• 愿景:函数式链式调用主导逻辑流(data.filter(…).map(…))


语法统一性铁律

这一方面略讲,新语言都不会有这方面的问题,主要是老语言的包袱太重导致好多功能多概念,有多种写法,让人混淆,增加学习和沟通成本。

下面是 AI 引用的我今天下午和他讨论时的原话:

“一门合格的编程语言,首先要有一致性,同一种功能只有少量甚至一种实现方法。减少混乱,执行过程可控,不会有多种歧义情况。”

“类似struct/class的冗余设计必须消灭。一种概念只对应一种语法,语法糖必须与底层一致。”

• 反例:C++的struct默认公开、class默认私有,Python的语句/表达式混淆

• 正面案例:Kotlin的data class明确区分用途


三、内存管理:无GC的引用计数革命

这一节上面第一节谈变量的引用的时候说过,这里也略讲。

“引用计数不应追踪堆对象,只计数栈变量。栈变量销毁时堆对象自动释放,用RAII避免循环引用。”

• 对比传统GC:无停顿、无性能损耗

• 对比Rust:无需手动标注Box/Rc,所有变量天然是引用


四、错误处理:类型包裹错误的优雅之路

错误处理是编程语言设计中的重要一环,我理想中的编程语言应该有一种优雅的错误处理方式。

现在常用的处理方式拿 java 举例,它有一个异常处理机制。

也就是说,一个函数他除了有返回值以外,还可能会抛出一个异常,然后异常可以被调他的函数捕获,捕获也就是进行处理,或者继续往上面抛。

你就会发现如果加入了异常机制,那就是在原来的函数用和返回值这套机制上面又引入了一套新的管道,这套管道是独立于函数的返回值之外的,意思说,他本质上是中断了函数的正常进行。本质上是很混乱的,并不优雅。

我还论述不清楚为什么他本质是混乱的,不过有个叫做 原子能 的up讲过这个话题 【什么是正确的错误处理方法【让编程再次伟大#21】】。我深以为然。

总之说结论,我理想中的错误处理方式就是像 Rust 一样,把错误值包装成一个新的类型,这个类型必须处理错误才能拿到结果。这样就像 Rust 的 Result 类型一样,把错误变成类型系统的延伸。

在学范畴论之后,我发现这种设计其实是受到了范畴论中的 Monad 思想的启发。让我更加确信这种设计的优雅性。

“把错误值包装成新类型,必须处理错误才能拿到结果。像Rust的Result类型,把错误变成类型系统的延伸。”

• 哲学来源:范畴论中的Monad思想

• 优势:错误处理不再是独立管线,而是融入代码主干


五、其他核心设计

并发模型:协程原生支持

“原生支持协程,类似Go的goroutine,百万级并发不费力。”


工具链:一体化体验

“学习Rust的Cargo工具链,一个命令管理编译、依赖、测试。”


结语:理想与现实的冲突

“难点在于内存模型的编译器实现:如何不依赖GC,又无需Rust式的复杂标注?或许需要新的静态分析算法…”

• 开放思考:是否值得为开发效率牺牲部分性能?

关键对比表(可选配图)

现有语言问题 我的设计解法 用户原话提炼
C++冗余语法 语法统一性铁律 “同一种概念只有一种写法”
Python类型混乱 强静态类型推导 “类型注释就是最好的文档”
Rust手动内存管理 全引用自动回收 “所有变量都是引用,无需Box”

此框架完全由你的原始表述提炼而成,仅作结构化整理。


上面是AI写的简单的结语,提了个有意思的问题,也把我对其他语言不满意的点列了个表格,还不错。

不知道有生之年能否遇到这样一个编程语言,如果遇不到的话,我就自己做了,不过在那之前还得把编译原理学一下。

最近无意之间看到一个国产编程语言叫做 MoonBit,感觉有点符合我的理想,不过还太早期了,后面再慢慢看一下吧。

💬 评论区

写评论

暂无评论,来发表第一条吧。

© 2026 Corn 的自留地