编程语言闲谈

编程语言闲聊

本篇blog只是有感而谈 不全面 不充分

函数式编程

以下函数式编程 简称FP

初等印象

  • 函数是一等公民
  • 没有副作用
  • 不修改状态
  • 引用透明

首先函数是一等公民 什么意思 其实这句话很容易在各个地方看到,但是可能也很难理解到所谓一等公民的意义,在初始的感觉就是不用写类,可以直接写函数。后来又能理解一点函数是一等公民就是跟其他对象都一样,可以当做函数传递,也可以赋值。这其实是涉及到整个FP编程核心的一个概念,为什么要这么设计,在下文会有说明

没有副作用,不修改状态,引用透明都是因为一个东西的存在的取消实现的,这个叫中间状态。这里我就懒得说明了 贴一段写的不错的

在函数式编程中,一个变量一旦被赋值,是不可改变的。没有可变的变量,意味着没有状态。而中间状态是导致软件难以管理的一个重要原因,尤其在并发状态下,稍有不慎,中间状态的存在很容易导致问题。没有中间状态,也就能避免这类问题。无中间状态,更抽象地说是没有副作用。说的是一个函数只管接受一些入参,进行计算后吐出结果,除此以外不会对软件造成任何其他影响,把这个叫做没有副作用。因为没有中间状态,因此一个函数的输出只取决于输入,只要输入是一致的,那么输出必然是一致的。这个又叫做引用透明。这些不同的名词差不多都在讲一个意思。

于是这可能就是大家对FP的初等印象了。至于什么写的快,可读性强。易于并发这些特点的原因就涉及到FP中核心的设计理念了,不过上面的这些说明大概可以讲清为什么FP易于并发了

下面就开始讲语言设计本身了


高阶函数

什么是高阶函数呢,能够接受一个或多个函数作为入参又或者返回参数是函数的这么一种函数,在java的stream中每个中间函数都是这样的格式,类似于map,filter,reduce等等

1
2
3
> > def sumInts(a: Int, b: Int) = sum(id, a, b)
> >
>

高阶函数提供了一种函数级别上的依赖注入(或反转控制)机制

在上面的例子里,sum函数的逻辑依赖于注入进来的函数的逻辑。很多GoF设计模式都可以用高阶函数来实现

如Visitor,Strategy,Decorator等。比如Visitor模式就可以用集合类的map()或foreach()高阶函数来替代。

函数式语言通常提供非常强大的集合类(Collection),提供很多高阶函数,因此使用非常方便

柯里化

什么是柯里化 其实在Stack Overflow上有很好的说明

大概就是将一个函数多个参数转换为一个参数的过程,不然函数是很难进行类似stream方式流动的

闭包

闭包就是在函数中引用了自由变量的函数。这个引用的自由变量将会与这个函数一直存在,即使离开了这个环境。

js应该同学应该比较熟悉,闭包在js中可以简单的理解为函数套函数,外面的函数作用域消亡了,但是内部引用外部函数作用域的值还保留。这就出现了闭包

惰性求值

这种就是按需取值,在我们使用cache的时候也有这种思想,尽量减少服务器的压力

这样就可以通过避免不必要的求值提升性能

深入到思想

函数式编程的目的是什么呢?

模块化

我们需要透过表象看到更深的抽象层次,例如结构化编程和非结构化编程的区别,从表面上看比较大的一个区别是结构化编程没了“goto”语句。但更深层次是结构化编程使得模块化成为可能。像goto语句这样的能力存在,虽然会带来一定的便利,但是它会打破模块之间的界限,让模块化变得不容易。而模块化有诸多好处,首先模块内部是更小的单一的逻辑,更容易编程;其次模块化有利于复用;最后模块化使得每个模块也更加易于测试。模块化是软件成功的关键所在,模块化的本质是对问题进行分解,针对细粒度的子问题编程解决,然后把一个个小的解决方案整合起来,解决完整的问题。这里就需要一个机制,可以将一个个小模块整合起来。函数式编程有利于小模块的整合,有利于模块化编程。

这里就涉及到了一个上文中提到的特性了 叫延迟求值 ,延迟求值与函数式的目标模块化的关系是有关系的

在一个数据量很大的场景中,在大部分语言都是及早求值的情况下,我们很难一下load如此巨大的数据到内存中,按照函数式编程缓求值的想法,我们可以先执行到第一个高级函数,如filer(),根据filter()的需要,再readFile去读相关的内容,用多少,读多少,对内存没有压力,并且很好的实现了两个模块的分离.如果走的是传统的及早求值,我们可能就是得先readFile和filter写在一个函数里,边写边读,但是这样就无法进行模块化了

为什么说函数式的可读性更强,因为函数式编程与传统的命令式编程的不同,传统的命令式编程是在对机器语言上面的抽象,而函数式编程则是对数学语言的抽象。而数学语言更加接近人的语言。更可读。所以我们平时说,函数式描述的怎么做,而命令式描述的如何做,这是关键的原因

这里的数学语言其实是范畴论,但是这个其实如果追到这里就很容易偏离了方向,范畴论已经很难与实际的代码能够牵连在一起了,本身就是数学上的概念了,其中涉及到的物件与态射的关系本身就很难理解,更不用说关键的函子的变化与细分,会很容易的让人陷入数学的深坑中

我个人是推荐看点轻松愉快的科普文章,如一些听起来不错的有逼格的名词 如Y combinator 图灵停机问题,不完备性定律等 这里有篇很不错的科普文章

聊聊一些语言

typescript

拯救弱类型的js超集

由于我写前端的机会不多 还是在学习angular2的时候入门了ts,只能泛泛的谈一下Typescript这门语言

以下Typescript简称为ts

首先最重要的就是类型系统,在Ts中对类型的进步是比较大的 不仅引用了传统的java中的显示说明类型的方式 还有联合类型这种语言特性界的新宠。

当然在Ts中对es6中新引入的OO概念进行了扩展,引入了新式的interface,这与传统在java中interface不一样,在ts中的接口是一个非常灵活的概念,除了可用于对类的行为进行抽象外,也可以对外的形象进行描述。

比如 如下这种写法

1
2
3
4
5
6
7
8
9
10
interface Person {
name: string;
age: number;
}

let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
};

最重要的当然还是类型系统这里,很欣慰的见到了泛型这个东西,虽然它的泛型实现还是相对简单的 并没有到达java1.5更新中的? super extend这种相对复杂的OO概念。但是也是一个非常好的开始,不知道现在社区是否已经支持到了这个级别,我们还可以看到在c语言中涉及到类似于结构体的一种写法 类型别名

同样的还有联合类型这种语言界新宠

1
2
3
> > let myFavoriteNumber: string | number;
> >
>

在关键的一些的错误的时候,typescript的编辑期静态检查也可以通过类型推论做到类型检查。在编译期报错。

如下就会报错

1
2
let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

总之我是非常推荐前端同学学习这门语言的,对类型的补充是对js本身强大的增强。随着现在数据的状态在前端部分的扭转的剧增,逻辑也会随着而复杂,一些js的本身的弱类型特性已经支持的很困难了。ts可以说的上是一门非常优秀的选择

劣势:

拥有一定的学习成本

如果是前端人员可能要学习一些OO的概念

不够JS灵活

kotlin

工程上的裱糊匠

这是一门真正的工程语言

文档

kotlin中文社区的文档

Kotlin-koans练习题

对kotlin语言的误解在哪里

很容易陷入对kotlin的误解中,因为kotlin在客户端领域目前是十分的流行,列如安卓,或者idea本身都是由kotlin写的。但这并不是说kotlin在服务端领域就不够优秀,反而这些年也是逐渐的上升的趋势

我们在讨论这种语言的时候,都会在考虑一个问题,就是对我们的帮助的程度问题,如果说是一门与我们工作相邻的近亲语言的话,那对我们的帮助肯定是相对远亲的语言一般来说是要大的,kotlin就是这么一门与java血缘关系非常近的语言。

首先就是java与kotlin本身就是无缝调用的,那这么一个情况下,kotlin就是可以使用java原本的库。也就很少存在库不够的情况了

第二个优势就是idea本身,对kotlin的支持也是非常到位的,由于kotlin本身就是由jetbrains开发的。所以在各种方面的支持都很到位,如空的安全监测这些特性

另外一方面为了让java人员能够快速转到kotlin中,idea提供了java到kotlin的自动convert工具,当然这个东西充满了缺点,

这其实也是图灵停机问题的一种表达吧

当然它还有很多新的优秀的特性,正如开头所言,这是门工程的裱糊匠语言,它充满了对工程的思考,不像scala这种还带了一点学术味道的语言,更不像haskell这种纯FP,基本很难工程的语言。它对很多语言中语言特性都有采纳,例如我之前在c++中才看到过的操作符重载,string-template,一些类型推断,跟typescript的很像的智能参数,减少java中重载这种代码的出现。同样的作为一门相对FP的语言,它的集合库也是比较强大的。在原本java的基础上也是有这自己的集合扩展,如flod,partition这些。也加入了延迟求值这个FP的标配

当然也有自己独特的对工程的理解,如很出名的空安全,类似java中一个注解lombok的data-class,java10才支持的var以及一个final的val类型表达,这也是类型推断的一种新的技术思考引用。

由于新特性太多,一一说明实在过累。只能这样大概说下这些用的很舒服的地方,还有一些细节的地方,如lambda表达式的写法改进,在lambda中的特殊占位符lt

可以说这是一门充满了惊喜了语言,如果一个人对代码本身感兴趣,我觉得这门语言的学习是十分值得的。

劣势

拥有一定学习成本

对于业务的支撑不够明显

在java调用kotlin存在一定的坑

python

一门哲学的语言

人生苦短 我用python

对于python这门语言我就随意的吐槽了,毕竟是一门很简单的语言。

推荐一本我入门的书,<简明python>,可以很快的入门。

这是一门朴素的语言,它不像c++这样各种魔幻的范式,它干净,整洁。专注目的。有一种实现的方式就不会实现第二种。

有一首这门语言的设计哲学之诗

它同样的也是无类型的,也是函数式的

下面说一点这门语言用舒服的地方


无须声明类型 没有分号,lambda的写法很快捷,舒服。

字符串模板函数不错,我见过的其他语言的string_template都比java强

集合库简单好用

不是纯OO 不用写类直接写function

文档写法也很快 pip支持也比maven快捷

内置函数简单 比如IO来说 不用像java关心字节流 字符流 buffer包装 一个open函数直接OS层面类似Shell操作就可以

linux原生支持 拉出来就运行 可以代替shell了

也有很多劣势的地方


空格对齐

不写类还得按照顺序调用function

太简单都会忘记了条件表达式后面得跟冒号

python2与python3的断崖升级 python2不能直接在python3环境运行

注意编码

缺点:

过于弱类型 后期的维护问题

语言本身的哲学过于沉迷于逻辑并不复杂的开发困难

对于语言本身的限制的放开

对于性能的漠不关心