2009-10-29

Pure v.s Impure - 2

前一篇文章中缺乏示例,一眼看下去可能不太好理解。这次用几个简单的例子说明一下。

"Pure"总是和下面几个词联系在一起:"referentially transparent","no side-effect"。基本上来说,它们表达的同一个意思。直观的意思就是:给定相同输入,一定会得到相同的输出结果。更正式一点的说法就是:给定一个函数f,把所有调用f的地方换成该调用的结果不会改变程序的意义。举个例子:假如f(3) = foo,把程序总所有调用f(3)的地方换成foo不会改变程序的结果,反之亦然。

更具体一点的例子,假设有个C函数,如下:
int add(int x, int y)
{ return x + y; }

显然,它是个没有side-effect的函数。好了,现在多了个需求:统计"add''函数的执行次数。这在允许side-effect的语言(如,C/C++等大多数主流语言)中简直是小菜一碟,加个全局变量就行:
static int count = 0;
int add(int x, int y) { count++;
return x + y; }

注意:此时add函数已经是个具有side-effect的函数了。我们无法把程序中所有出现add(1,2)的地方都替换为3!作为一个side-effect,该函数还修改了count的值。做了替换之后,将使得我们统计执行次数的努力付诸东流。

那么,在Haskell之类的纯函数式语言中,如何做到这种统计功能呢?最先的add函数可以写出如下方式:
add x y = x + y


因为不允许side-effect[1],那么最简单的方式就是引入一个额外的参数作为初始的调用次处,然后add每次都返回更新后的调用次数:
add x y cnt = (x + y,  cnt + 1)


郁闷的是,这样一来函数add的输入、输出都修改了,程序中所有调用到该函数的地方都要做相应修改。不过千万不要气馁,Haskell中提供了解决之道 - Monad。

[1] 对于习惯print调试大法的程序员来说,很可能会不习惯Haskell,因为向一个pure的函数中添加print语句是不允许的。Haskell用Monad来分隔pure/impure的行为。似乎这也是一些程序员选择OCaml/Standard ML而放弃Haskell的原因之一。但Pure的系统中,编译器有更多空间去做并发方面的优化。

标签:

2 Comments:

At 09:22, Anonymous kk said...

add x y cnt, 增加了一个参数。
如果这样可以的话,之前的方法也可以通过增加一个参数的方法,来保证pure

不过文中没有解释monad,估计这个就是不用添加多余参数的方法。

 
At 10:08, Anonymous live4thee said...

在Impure的语言中,添加全局变量的方法简单、直接且方便。
解释Monad需要读者对Haskell有一定的了解。以后讲。:-)

 

发表评论

<< Home