从变量不可变到编程范式

来源:互联网 发布:我知谁掌管明天 歌词 编辑:程序博客网 时间:2024/04/29 11:56

1、众说纷纭的变量不可变

近期在论坛中有一个很有意思的讨论,erlang中的变量不可变原则的原因到底是什么,似乎各个二郎君还没有一个统一的看法,主要的观点有两个:1、出于并发的需求,防止变量被二次赋值而导致各种并发问题;2、erlang的模式匹配导致,因为本身并不存在赋值操作,而模式匹配的规则就是不可对变量二次绑定值。

在我刚学erlang的时候,我曾经对第一种说法深信不疑,erlang变量不可变原则,感觉上确实可以很巧妙的解决并发编程中对共享数据并发访问的问题,因为数据不再被二次赋值,也就没有了脏数据问题。后来随着学习erlang越久,渐渐发现了这种说法的荒谬之处,最基本的,erlang本身在用户(程序员)角度,理论上并不存在共享内存的问题,每个进程的变量是属于进程私有空间的,也就是说即使变量可以二次赋值,也只能在单个进程中执行,所以根本不需要用变量不可变这点来保证并发性。在否认了第一种说法后,我开始倾向于第二种说法,变量不可变是erlang本身的模式匹配机制所决定,但是随着阅历增加,我接触了大量其他函数式编程语言,诸如haskshell、schema、lisp等等,我发现在不少的函数式编程语言中,都存在变量不可变机制,也就是说,这个规则并非erlang专有,所以第二种说法也不能解释。

2、源码中的答案

最先想到的就是从代码编译入手,因为erlang虚拟机是解释执行中间字节码文件的,测试代码如下

-module(test).-compile(export_all).test() ->    A = 1.

编译,查看中间代码,如下

{function, test, 0, 2}.  {label,1}.    {line,[{location,"d:/Products/KingServer/test.erl",4}]}.    {func_info,{atom,test},{atom,test},0}.  {label,2}.    {move,{integer,1},{x,0}}.    return.

如上所示,在编译erl代码的时候,编译器给每一个语句都生成了一个label,其中lable2就是匹配语句 A = 1 的字节码表示,从这里可以看出来,所谓模式匹配在编译的时候,是用了{move, XXX, XXX}来表示的,而erlang解释执行move的时候的具体代码,opt/erts/emulator/beam_emu.c中可以查看到

#define Move(Src, Dst, Store)      \   do {                            \       Eterm term = (Src);         \       Store(term, Dst);           \   } while (0)

do_increment:    increment_val = Arg(1);    if (is_small(increment_reg_val)) {Sint i = signed_val(increment_reg_val) + increment_val;ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i));if (MY_IS_SSMALL(i)) {    result = make_small(i);    StoreBifResult(3, result);}    }    live = Arg(2);    HEAVY_SWAPOUT;    reg[live] = increment_reg_val;    reg[live+1] = make_small(increment_val);    result = erts_gc_mixed_plus(c_p, reg, live);    HEAVY_SWAPIN;    ERTS_HOLE_CHECK(c_p);    if (is_value(result)) {StoreBifResult(3, result);    }    ASSERT(c_p->freason != BADMATCH || is_value(c_p->fvalue));    goto find_func_info;}
#define StoreResult(Result, DestDesc)\  do {\    Eterm stb_reg;\    stb_reg = (DestDesc);\    CHECK_TERM(Result);\    REG_TARGET(stb_reg) = (Result);\  } while (0)

如上所示,在执行move操作的时候,实际上是创建了一个中间变量Eterm,然后把Src赋值给这个中间变量,最后再把中间变量的值存入目标Dst。在存入的时候,如果目标已经有值且不相等,那么就会抛出异常BADMATCH。从源码来看,erlang中的变量不可变机制,毫无疑问确实是由于模式匹配的原因造成的,可是,如何解释其他语言中也存在这种机制呢?

3、函数式语言的编程范式

函数式编程是种编程范式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是 λ 演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。和过程化编程相比,函数式编程里,函数的计算可随时调用。

以上摘自百度百科,所谓编程范式就是编程的方法论,而函数式编程的方法论就是:运算过程尽量写成一系列嵌套的函数调用。也就是说,erlang的每一句代码,其本质都是一个函数,都是有返回值的,他们并不是没有返回值的操作。这种做法的带来的方便就是,函数保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不会修改外部变量的值。所以,所谓防止并发问题不是erlang模式匹配的成因,而是结果。当然,第二种说法也不全对,erlang变量不可变的原因主要是因为遵循函数式编程范式,模式匹配是手段不是目的。

4、总结

对于已经熟悉的东西,如果存在疑问,那还是多思考,说不定会有惊喜

原创粉丝点击