编程语言概述

众所周知, 计算机这个行业是不断发展的。 编程语言就是其中之一,要么是出现新的语言,要么是出现这个语言的特性升级。 其他领域,比如法律和医学, 知识更新迭代的速度远比不上编程领域.
让我来沏一杯茶,闲聊一下编程语言这个老生常谈的话题.

  • 编程语言历史
  • 什么是编程语言?
  • 编程语言的种类
  • 设计一门编程语言需要解决什么问题?
  • 编程语言的未来

编程语言历史

  • 1842年:第一个程序

    Ada Lovelace 在笔记本上写了一些计算机指令一样的东西,后来被公认是世界上的第一道计算机程序,只是那时计算机还没有问世。

  • 1957年: FORTRAN - 第一个程序员可以使用的程序

    John Backus 发明了 FORTRAN,这是第一门真正意义上有程序员在使用的编程语言

  • 1959年: COBOL

    Grace Hopper 发明了第一门企业级的编程语言,叫作“common business-oriented language”,简称 COBOL

  • 1964年:BASIC

  • 1970年:Pascal

  • 1972年: C语言 - 为Unix的出现奠定了基础

  • 1980年: Smalltalk - 第一个支持面向对象的语言

  • 1983年: Ada

  • 1983年: c++

  • 1986年: Objective-c

  • 1987年: Perl

  • 1991年: Python

  • 1993年: Lua

  • 1994年: PHP

  • 1995年: Ruby

  • 1995年: Java

  • 1995年: Javascript

  • 2001年: C#

  • 2009年: Go

  • 2010年: Rust

  • 2012年: TypeScript

  • 2014年: SWift

根据公开的语言排行榜,排名前十的语言都是70年代以后诞生的语言. 这里面, C语言在底层领域是霸主,Java在应用领域独占鳌头, Python是数据处理领域的翘楚, JS是Web开发领域的不二人选.

什么是编程语言?

编程语言和中文,英文都有相似的地方,比如要符合一定的语法,但又稍许有些不同,比如人和人完全可以用汉语交流所有问题, 但计算机却没有这样一个语言能解决所有问题.
编程语言是给开发者使用的一种模型工具. 它符合一定的规范, 以某种方式解决了特定领域的问题。 这种方式背后的思想包括且不限于

  • 抽象
    • 抽象的目的是为了提供统一的方式来处理现实中的概念和行为
    • 一个例子:在国家层面,不管男女老幼,都统一叫做公民,公民这个词就是一个抽象,在计算机领域可以表现另外一种名字: ID
  • 隔离
    • 软件开发的根本目的是为了控制复杂度。而分离关注点是其中的一个有效方式
    • 一个例子: 国家以秦岭淮河为分界线,将中国的地域分为南方和北方。
    • 隔离抽象和实现
    • 隔离重要和非重要
    • 隔离不变和变化
  • 组合
    • 这个世界上大多数系统是基于还原论的,软件系统也不例外。
    • 一个系统如果是可以还原的,那么就是可以组合的.
    • 一个生活中例子:不同的县组成了市,不同的市组成了省,不同的省组成了国家.

本文为了方便讨论, 讨论的领域仅限于企业级应用领域

编程语言的种类

编程语言的种类远多于数据库和操作系统的种类,正如武林中有各种各样的门派一样,编程语言也有很多门派. 这些门派之间也是口水不断和相互鄙视的。

过程式编程

过程式编程的核心概念是结构体和过程(也可以称为函数)

  • 侧重于描述先做什么,后做什么,符合人的自然思维
  • 缺点
    • 函数是全局的,没有访问控制.
    • 项目规模变大了,代码会比较混乱,容易形成意大利面条的形式.

面向对象编程

面向对象的价值主张是一切皆对象。 对象包含状态和行为. OO的鼻祖是SmallTalk,

I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages (so messaging came at the very beginning – it took a while to see how to do messaging in a programming language efficiently enough to be useful)….OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP.

从上面这段面向对象设计的初衷我们可以看到

  • 对象类似网络的一个独立的节点
  • 对象隐藏了内部的状态
  • 对象和对象之间是通过消息方式来交互协作的

其中对象是一个独立的节点在不同的场景下,它可以是:组件,服务或者一个独立的系统.
要实现以上特性,必须做好下面的事情

  • 抽象 - 需要不断的总结和归纳名词,将名词放到特定的层次和类别, 也正应了命名是计算机的两大难题之一
  • 隔离 - 在良好的抽象基础上,才能进行很好的隔离. 这是一种分解思维.

基于以上特点我们可以编写任何复杂的系统. OOP是一种思想,OOP程序不一定要用OOP语言来写

这个世界上花花草草,各色人等都可以用名词来表示,所以围绕名词以及名词的行为来表达这个世界是很自然的事情。因为我们上学的时候无论是学语文还是学英文都是要从区分名词,动词,形容词等开始的。 所以围绕名词来描述和表达问题对人的认知来说,是丝滑般的自然.

一些符合面向对象思想的例子:

  • 猫是一种动物。 很多时候,我们要回答这个东西是什么
  • 审批流程,员工A想要请假,小组长需要审批,上面的经理也需要审批,这个流程是可以画出一个协作图. 可以想象一下函数式编程如何处理这个问题

反对OO的声音

  • 对象是有状态的.
  • 想要引用一个对象的行为,就必须把它相关的对象都引用进来

    你想要香蕉,但是得到的却是拿着香蕉的大猩猩和整个丛林

    拾遗
  • 相比于继承,优先使用组合是业界主流。
  • 一个大型应用项目,对对象进行正确的抽象,以及合理定义对象之间的关系意味这个大型应用已经成功了50%,在此基础上对对象的行为进行合理定义,意味着这个应用已经成功了70%
  • 面向对象和关系型数据库是好基友
  • 面向对象诞生在单核时代.
  • 一个类有私有和公开的部分,私有的部分是针对维护者,公开的部分是针对使用者。在使用者眼中看来,公开的契约或者接口就是全部的功能, 使用起来能降低使用者的认知负担. 以前的老式收音机,会有音量,调频,开关三个键,这三个键就是全部的功能,满足用户的所有需求.

函数式编程

函数式编程的价值主张是一切皆函数. 函数从语义上来说,强调的是行为。 它有两个核心原则

  • Stateless - 简而言之就是函数内部不保存任何状态.
    • 求解1+2+4,在过程式编程中,先算出1+2=3,作为一个值保存在变量中,然后再加上4得到最终结果7。
    • 求解1+2+4,在函数式编程中,函数接受输入1,2 返回结果3,然后将返回结果3和数据4作为输入传函数,最终返回结果7
  • Immutability - 输入的数据是不能被改变的. 有一个复印的函数,输入是原始照片,输出是复印件,函数执行完毕之后,原始的照片必须是完好无损的.

所有的函数式编程都必须遵守这两个核心原则. 函数式编程也是声明式编程的一种.

函数式编程用到的技术

  • 尾递归优化
  • map & reduce
  • 管道 - 函数可以按顺序组合. 其思想来自Unix, 如果将这种组合思想应用到宏观领域就是服务的编排
  • 递归 - 从直觉上定义和描述问题. 暗含了”做什么”的思想
  • 柯里化 - 将一个函数的多个参数分解为多个函数. 暗含了分解和隔离思想
  • 高阶函数 - 将函数A作为参数传入去,然后包装成另外一个函数再返回. 这里体现了分层和组合思想. 这是一个很重要的特性, 因为任何问题是可以分层次解决的.

一些符合函数式编程的例子:

  • 斐波拉契数列: f(x)=f(x-1)+f(x-2)
  • 对一组数据进行加工,排序,然后分组,然后过滤,然后转换,最终得到结果. 可以想象一下面向对象是如何处理这个问题的

函数式编程的优势:

  • 没有状态就没有伤害,很契合高并发场景.
  • 重构代码的时候可以基于Copy的方式.

反对函数式编程的声音

  • 保证不变性是有代价的

    对某个下标的数组元素的修改,就需要复制整个数组.

总结

函数式和面向对象都有各种的粉丝, 两波粉丝也是口水不断, 都是拿各自的优点来攻击对方的缺点.
正如我们前面提到的,这个世界上没有所谓的优点和缺点,只有特点.
脱离问题和场景谈优缺点都是耍流氓.

  • 当今的软件开发主要是在工程领域,而不是科学领域. 科学领域主要是研究理论和算法. 工程领域主要是解决现实中的业务问题. 根据二八法则,高水平的开发者只占20%, 一般水平的开发者占其中的 80% 。既然是工程问题,那么肯定有一大批开发者,这些开发者的水平也是参差不齐的。所以就需要一些相对简单的方式来满足大多数开发者写业务逻辑。而面向对象这种模型满足来80%的开发群体. 但这个理由还不是最充分的.
  • 如果软件的规模很大,量变就会产生质变. 就需要简单的方式来写业务。只有简单符合直觉的东西,才是可读的,可理解的,然后是可维护的. 面向对象满足着一点。这是一个事实型的结论。 市面上的大型软件没有一个是基于函数式构建的.
  • 写代码和写文章没有本质的区别. 写文章也要遵循一定的机构,比如总分总。写代码也是,但更严格. 代码是写给人看的,然后顺便可以执行.

设计一门编程语言需要考虑什么问题?

如果要实现一门现代编程语言,需要考虑哪些方面的问题? 从语言的能力的角度, 它需要有

  • 类型系统
    • 强类型系统
      • 如何表达基本类型? 整型,字符型,布尔型.
      • 如何表达非基本类型? 类和函数谁是第一等公民?
    • 弱类型系统
  • 常用的算法和数据结构
    • 顺序存储和链式存储。 比如数组,链表
    • 字典
    • 集合的各种操作。 比如sort, sum, map, filter…
  • 模块系统
    • 如何定义一个模块以及如何引入一个模块
  • 流程控制
    • 顺序,选择,循环
  • 异步
    • 如何优雅的处理异步
  • 进程和线程
    • 如何处理高并发问题 - 这里的搞并发是狭义上的高并发
  • 异常处理
    • 如果程序遇到非正常情况, 应该如何处理?
  • 时间
    • 任何语言都必须提供良好的API支持时间。包括时区转换,时间格式化等操作.
  • 网络
    • 如何收发消息? NIO还是AIO?
  • 正则
    • 这个是大多数语言都会内置的功能。 正则表达式是一种声明式语言
  • 文件
    • 各种各样的文件操作和解析.
  • 数据库
    • 如何连接数据库
    • 如何读写数据库
  • 内存的分配和回收
    • 如何合理的利用内存?

编程语言和架构是什么关系?

编程语言和架构不是一个层次上的事物。
从设计重要性的角度来看, 架构的选择 > 编程语言的选择.
编程语言不会决定架构的选择,但是会对架构的选择产生一些影响。 当选择一门编程语言的时候,选择的往往不是这门编程语言本身,而是选择的基于这门语言的框架和生态. 就好比,张三娶了李四,娶的不仅仅是李四,还包括李四的家庭.
在云原生时代,应用的基本单位是容器,你可以用Go来构建一个微服务,也可以用Java来构建一个微服务,当然,也可以用Nest.js来构建微服务,语言之间的口水之争会慢慢淡化。

编程语言的衍生品

我们选择的往往不是一门语言,而是与此语言相关的生态。
先来看看以下几个基本事实

  • Java毫无疑问是成功的.
    • 它有各种各样的规范
    • 它有Spring框架,稳定运行了20年,社区很活跃
    • 它可以运行在windows, linux, mac上
  • JS是成功的
    • JS 语言本身是有缺陷的, 使用起来不是那么简单.
    • 前端只有一门JS语言,你别无选择
    • 它有React框架,FaceBook背书,相关的社区很活跃
    • 可以运行在各种浏览器端
  • C#不是那么成功
    • .net 仅仅局限于windows. 虽然.net core 和.net5 现在可以运行在Linux上,但有相当一部分时间, .net是不能运行在linux上的.
    • .net没有一个像样的框架,比如Spring, 所以没有形成一个相应的活跃社区
    • .net 与微软这家公司息息相关.
    • C#的语言特性无疑是优雅的
    • C#的使用很简单
    • 国内大厂基本是Java系,C++系,或者Go系,没见过大厂使用.net的,典型的有京东和携程转Java

由上面的一些事实,我们可以得出一些结论

  • 一个语言的语法优雅,是一件好事, 但从技术决策的角度而言, 不是那么重要
  • 一个语言如果在一个领域没有替代品,而且这个领域又是刚需,那么这个语言活的很好,即使这个语言本身不好用.
  • 一个语言想要活的好,需要有一个重量级别的框架或者工具. 这个框架符合以下两个特点最好.
    • 这个框架是由大公司开发和维护的
    • 这个框架是开源的,有很多开发者参与,形成了一个活跃的社区

      以上的这两个特点意味着这个框架试已经踩过各种坑,形成了自己的最佳实践和规范,小伙伴们可以放心使用,睡个好觉.

作为一个技术决策者来说,选择一门语言,主要考虑两个问题

  • 要解决什么问题?总不能说要研发一个编译器,选择Java语言.
  • 这个语言背后的生态是不是强大?

一个框架再牛,那它还是只是个工具,要做到心中无框架,才能无招胜有招.

编程语言的未来