Think Different
Dave Thomas大神曾经把学习 Elixir 比成 Take the red pill。诚然,相对于传统的编程语言而言,函数式编程有点“异端”。实际上,恰恰是这些“异端”,可以领阅更多的编程思想,了解更多的编程模型。简而言之就是Think Different。
函数式编程
很多介绍Erlang,Hashkell等书籍都喜欢对读者宣传:学习下一代编程语言。下一代给人的感觉就是新生儿,好似他们都是刚刚发明的。实际上函数式编程语言是很古老的语言。甚至现在很多高级语言还没诞生,他们就出现了。
虽然很早就出现了,可是函数式语言一直在工业领域没什么地位,虽然学术方面成就颇高。可是随着现在网络应用的发展,函数式语言得天独厚的特性逐渐展示出强大的力量。也无怪乎媒体社区都在说下一代编程语言。
并发 并发
函数式语言被称之为下一代编程语言,主要原因还是对并发的处理。随着网络应用的发展,C10k的问题逐渐暴露。用户量增长迅速,网络应用再也不是支持几百人的那种小流量了。而是有可能处理上亿级别的数据。越来越多的服务需要高并发,高时实。而函数式语言几乎都天生支持并发。
函数式语言天生支持并发,主要源自函数式语言的特性,不可变性,函数没有副作用。这样的特性很容易构造分布式系统。
从现在计算机的发展来看,单核的CPU运算相当快了。并且根据摩尔定律,cpu的速度还会提升。可是,还会永远提升么?实际上,尽管cpu的还在升级,可是却很难有数量级的优势了。
也就是说,单纯的通过提高cpu的运算能力来提供服务的性能,将会成为发展的一个瓶颈。既然纵向方法遇到了困难,那么可以横向拓展嘛。
把依靠单核cpu的方式转变成使用多核来提高性能。可是,传统的编程模型中,并发多数是串行的方式。程序的进程通过共享内存来通信。多线程或多进程同步成为一个令开发者头疼的问题。
分布式
与共享内存的不一样的模型是分布式内存,即每个CPU有属于自己的内存,这些CPU内存可以分步在多台机器上,多台机器可以处理同样的计算任务。因此很容易横向拓展。Elixir和Erlang都采用Aacton的并发模型。
它们可以轻易的开启成千上万个进程(process)。这些进程和别的语言的进程不一样,是极其轻量级,甚至比线程还轻。同时,进程之间通过消息(message)通信。这样的模型,很符合我们的真实世界。
OOP的范式通过类、属性、方法定义对象的性质和行为。通过共享类或对象来做状态同步。这样的模型在工业力长盛很多年。Elixir所采用却不是这样,而是通过进程和消息来编程,这其实更符合自然界。自然的通信就是讲话,而不是通过共享大脑。
Elixir/Erlang
Elixir是一门函数式语言。Elixir中的数据类型都是不可变的。变量一旦被绑定了之后,就不能改变了(可以重新绑定)。对于列表[1, 2, 3]
,如果给他们当中的每一个元素都加 100 的时候,Elixir
会根据原列表拷贝一份,并带有新的值。原始的列表并没有被改变。你的任何操作,就不会影响引用了原始列表的代码。
因为数据的不可变,对数据的操作都需要拷贝一份数据,内存中将会有很多数据需要垃圾回收,这样会不会造成低效率呢?
直觉上这样的操作确实让人感到低效,可是Elixir却恰恰相反。因为Elixir中的数据是不可变的,因此当新建一个数据的时候,可以复用原来的数据。
iex(2)> list1 = [3, 2, 1]
[3, 2, 1]
iex(3)> list2 = [4 | list1]
[4, 3, 2, 1]
iex(4)> list2
[4, 3, 2, 1]
list1是一个列表,并且是不可变的,使用|
操作符创建了list2。直觉上会觉得先把list1复制一份,然后再和4组合创建成新的list2。实际上,并不需要复制list1,只需要把4和list1组合即可。毕竟list1是永远不会变的嘛。
对于内存中大量无用的值,需要被垃圾回收。现代的语言几乎都有这个机制,其实有的人甚至怀疑来及回收本身带来的开销比内存中无用的“垃圾”还大。
Elixir提供了一个很cool的解决方案。写代码的时候可以使用多进程,每一个进程都有自己的内存堆栈。应用程序的数据被分发在这些进程之中。因此,数据在分散在很多内存而不是一个内存中,垃圾回收操作的内存都相对小,因而效率会很高。也就是垃圾回收在小内存中搜索需要清理的数据比在一个很大的内存中搜索要快。
尽管我们一直说Elixir是函数式编程语言,可是至今还没介绍函数,未免有点尴尬。下一章将会介绍Elixir中的函数。