Language


编程语言是人类和计算机沟通的桥梁,是一套系统,一种思维方式或一种哲学表达
虽然它们大都很抽象,但相比机器码而言已经可以被人类理解,使用和创造了\

语言的维度

编程语言的演进并非单一路径,也并非单一目标;大致可以分为以下三个维度

抽象维度

这是语言演进的核心主线之一,目标是“逐步向人类自然的思维方式和表达习惯靠拢”
光谱的两端:机器和人类

极端核心视角关注点语言例子
机器端如何指挥硬件CPU寄存器、内存地址、栈指针、指令周期机器码、汇编语言
人类端如何描述问题业务逻辑、数学公式、数据关系、用户意图SQL, Python, 自然语言编程

演进

第一代:完全面向机器
  • 机器语言:直接操作寄存器
    • 这在70-80年代很常见,现在几乎不用了
    • 查指令表,找到电脑CPU的指令手册
    • 写程序,写一串二进制
    • 输入,通过“前面板拨动开关”或现代调试器,把这串数字塞进内存的某个位置
    • 运行,让CPU跳转到那个地址执行
  • 汇编语言:助记符
    • 汇编器把汇编语言转换成二进制
    • 查指令格式、寻址模式、中断调用、寄存器用法

这个阶段,人在说机器的话

第二代:面向硬件操作,但开始封装
  • C语言:巨大的飞跃。不需要再直接操作寄存器,可以写a = b + c。编译器会帮你搞定如何把bc从内存移到寄存器、做加法、再存回去
  • 但是,你仍然需要思考”内存布局“、”指针“、”如何释放内存“。思维仍然被硬件结构(栈、堆)所限制

这个阶段,人在用机器能高效执行的”结构化“方式思考,但表达上已经接近数学了

第三代:面向人类操作,脱离硬件
  • C++/Java/C#:引入了”类“、”对象“、”继承“。这是在用人类社会的”类“和”关系“去建模世界,让机器来模拟人类的概念
  • Python/Ruby/Lua:更进一步,引入了动态类型、垃圾回收。当写下list.append(5)时,完全不用考虑”数组空间够不够?要不要realloc?”。机器在后台默默帮你处理了繁琐的底层工作

这个阶段,语言开始主动迁就人的思维习惯

第四代及以后:面向特定问题,声明意图
  • SQL:比如SELECT name FROM users WHERE age > 18。只是在声明想要的结果,完全没有告诉计算机要怎么做。这是“趋近人类”的极致表现之一:只管要什么,不管怎么实现
  • 函数式语言(Haskell, Lisp):写sum = foldl (+) 0 list。这是数学上的“递归折叠”概念,非常抽象和高级
  • 未来/实验性语言:用自然语言(英文、中文)写自然语句,或用图形拖拽编程

范式维度(思维模型)

  • 命令式:先做A,再做B,再做C
  • 声明式:我需要结果是X
  • 函数式:用数学函数的组合和变换来处理数据

安全维度

Rust并不比C更“趋近人类语言”,它甚至更难学。但它在趋近“让编译器帮人检查内存错误”这个目标,这是另一个方向上的“趋近人类”——趋近于人类的“不犯错”和“省心”

工具论

很多人把语言当作工具,我认为这种工具论是片面的,这取决于你站在哪个角度看问题

  • 从老板、项目经理的角度看这句话100%正确。编程语言就是为了交付产品、解决问题存在的工具
  • 从语言设计者、计算机科学家的角度看这句话非常片面。编程语言是思维的载体、数学的衍生物、甚至是艺术
  • 从普通程序员的角度看,这句话即是真理,也是陷阱

语言即世界观

如果编程语言只是工具,那么换一个工具应该只是手感问题
但现实中,语言深刻影响甚至限制了你解决问题的方式
这就是著名的Sapir-Whorf假说(语言相对论)在编程世界的体现

  • 用C语言思考:要时刻想着“内存怎么管理”,“指针指向哪里”,“这块数据怎么布局”。产生的解决方案往往是硬件导向的
  • 用Lua思考:会想“只需要一个Table,随便往里塞数据和函数就行”。解决方案往往是灵活、动态、适配导向的
  • 用Rust思考:会想“这个数据的生命周期如何”,“所有权”,“编译器能否通过借用检查”。解决方案是安全与所有权导向的

比如解决一个同时修改共享数据的问题

  • C++语境下:lock, Synchronized
  • Rust语境下:从编译期就避免这种设计,或者使用Mutex但配合所有权系统

语言并不是中立的。不同的语言就像不同的人,时刻面对着不同的环境,遇到相同的问题有着不同的反应

如果把编程语言仅仅看作工具,忽略了它以下几个重要的维度

它是一种思维训练

学习编程语言,其实是在学习如何精确、无歧义地描述一个过程。这种计算思维(分解、模式识别、抽象、算法)的价值远超语言本身

它是知识沉淀的载体

编程语言里封装了几十年甚至上百年的计算机科学智慧

  • 垃圾回收(GC):自动管理内存,背后是图论和指针追踪算法
  • 函数式编程(Map/Reduce):本后是lambda演算
  • 泛型:背后是类型论

它是社区和文化的载体

  • Python社区:强调”用一种最好、唯一的方式做一件事“(哲学)
  • Ruby社区:强调”程序员要快乐,有多种方式“(美学)
  • C++社区:强调”你不用的东西,不需要为其付出代价“(零成本抽象)

这些文化会影响你编码风格、沟通方式甚至价值观

工具论的积极作用

对于绝大数商业软件开发来说,工具论是工作层面的真理
老板关心的是功能能不能快速上线,成本多少,是否稳定;客户关心的是App卡不卡,会不会闪退
在这种语境下,用哪种编程语言,就是在选工具,目标是修好机器

把编程语言仅仅当作工具,会让你成为”码农“;而认识到语言是思维的延伸和知识的载体,才能让你向”工程师“或”计算机科学家“迈进

执行模型

很多时候会听到C/C++是编译型语言,Python是解释型语言
这让很多人误认为C是编译型语言,Python是解释型语言,但这其实是历史习惯说法,不是严格定义
需要明确:语言 =/ 必须用某种方式运行 编译型语言/解释型语言不属于语言本身的范畴,它们不是语言的“本质属性”,更是属于实现方式/运行模型/执行策略
C也可以被解释执行(比如用解释器),Python也可以被编译(比如编译成字节码.pyc,甚至用PyInstaller编译成可执行文件)
现代编程语言大都是混合体,几乎所有的现代语言都是“编译 + 解释 + JIT”的混合模型

语言实际情况
C/C++编译为机器码(AOT)
Java编译 -> 字节码 -> JVM解释 + JIT
Python编译为字节码 -> 解释执行
JavaScript解释 + JIT(比如V8)

主流执行模型

  • AOT编译(Ahead-of-Time)
  • 解释执行(Interpreter)
  • JIT编译(Just-In-Time)
  • 混合执行(Hybrid VM)
  • 源码转译(Transpilation)
  • 字节码虚拟机模型(Bytecode VM)
  • Trace JIT/自适应优化(Adaptive Execution)

AOT编译