diff --git a/a-week-with-elixir/README.md b/a-week-with-elixir/README.md index 7c2005d..852d3c4 100644 --- a/a-week-with-elixir/README.md +++ b/a-week-with-elixir/README.md @@ -4,11 +4,10 @@ 译序 ----------------- -作为`Erlang`之父_Joe Armstrong_,对`Erlang VM`上的新语言`Elixir`给出很精彩评论和思考。在『特定领域的专家的专业直觉』、『编程语言设计的三定律』、『管道运算符避免恶心代码』、『`Elixir`的`sigil`引出的程度语言如何定义/解释字符串』等等这些讨论上,能强烈感受到_Joe Armstrong_老黑客风范。 +作为`Erlang`之父_Joe Armstrong_,对`Erlang VM`上的新语言`Elixir`做了很精彩评论和思考。在『特定领域专家的专业直觉』、『编程语言设计的三定律』、『管道运算符避免恶心代码』、『`Elixir`的`sigil`引出的程序语言如何定义/解释字符串』等等这些问题的深入广博的讨论及其个性鲜明又幽默诙谐的行文风格,都能让我强烈感受到_Joe Armstrong_的老黑客风范。 -[自己](http://weibo.com/oldratlee)理解粗浅,而本文讨论是语言设计,且作为老一代黑客的作者计算机领域中那被我们现在不再要去理解使用的主题和思想(如`Prolog`/`DCG`、`Lisp`/宏、`sigil`、不可变闭包、语言设计的兼容性)真是信手拈来,翻译中肯定会有不少不足和不对之处,欢迎建议([提交Issue](https://github.com/oldratlee/translations/issues))和指正([Fork后提交代码](https://github.com/oldratlee/translations/fork))! - -PS:为什么要整理和审校翻译 参见 [译跋](translation-postscript.md) +[自己](http://weibo.com/oldratlee)理解粗浅,而本文讨论是语言设计,且作为老一代黑客的作者对计算机领域中那些我们现在不再要去使用理解的主题和思想(如`Prolog`/`DCG`、`Lisp`/宏、`sigil`、不可变闭包、语言设计的兼容性)又真是信手拈来,翻译中肯定会有不少不足和不对之处,欢迎建议([提交Issue](https://github.com/oldratlee/translations/issues))和指正([Fork后提交代码](https://github.com/oldratlee/translations/fork))! +PS:为什么要整理和审校翻译 参见 [译跋](translation-postscript.md)。 ------------------------ @@ -21,7 +20,7 @@ PS:为什么要整理和审校翻译 参见 [译跋](translation-postscript.md - [与`Elixir`相处的一周](#%E4%B8%8Eelixir%E7%9B%B8%E5%A4%84%E7%9A%84%E4%B8%80%E5%91%A8) -- [上周我下载了`Elixir`然后开始学习……](#%E4%B8%8A%E5%91%A8%E6%88%91%E4%B8%8B%E8%BD%BD%E4%BA%86elixir%E7%84%B6%E5%90%8E%E5%BC%80%E5%A7%8B%E5%AD%A6%E4%B9%A0%E2%80%A6%E2%80%A6) +- [上周我下载了`Elixir`然后开始学习](#%E4%B8%8A%E5%91%A8%E6%88%91%E4%B8%8B%E8%BD%BD%E4%BA%86elixir%E7%84%B6%E5%90%8E%E5%BC%80%E5%A7%8B%E5%AD%A6%E4%B9%A0) - [0. 编程语言设计的三定律](#0-%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%E8%AE%BE%E8%AE%A1%E7%9A%84%E4%B8%89%E5%AE%9A%E5%BE%8B) - [1. 在源文件中没有版本](#1-%E5%9C%A8%E6%BA%90%E6%96%87%E4%BB%B6%E4%B8%AD%E6%B2%A1%E6%9C%89%E7%89%88%E6%9C%AC) - [2. `fun`和`def`不同](#2-fun%E5%92%8Cdef%E4%B8%8D%E5%90%8C) @@ -34,7 +33,7 @@ PS:为什么要整理和审校翻译 参见 [译跋](translation-postscript.md - [9. 额外的符号](#9-%E9%A2%9D%E5%A4%96%E7%9A%84%E7%AC%A6%E5%8F%B7) - [10. 奇怪的空白符](#10-%E5%A5%87%E6%80%AA%E7%9A%84%E7%A9%BA%E7%99%BD%E7%AC%A6) - [11. 闭包行为完全正确 —— 哦耶](#11-%E9%97%AD%E5%8C%85%E8%A1%8C%E4%B8%BA%E5%AE%8C%E5%85%A8%E6%AD%A3%E7%A1%AE-%E2%80%94%E2%80%94-%E5%93%A6%E8%80%B6) -- [最后](#%E6%9C%80%E5%90%8E) +- [结束语](#%E7%BB%93%E6%9D%9F%E8%AF%AD) @@ -59,23 +58,23 @@ _Dave_对`Elixir`很感兴趣,在他的书里这样写道: > 但在几个月前,和_Corey Haines_聊了一次,在如何不用那些学院派的书给大家介绍哪些有吸引力的函数式编程概念这个问题上诉了些苦。 > 他告诉我再去看看`Elixir`。我照做了,有了第一次看到`Ruby`时那样的感觉。 -我能理解这种感觉,一种先行于逻辑的内心感性的感觉。就像我知道一件事是对的,我却并不知道我是如何和为什么知道这是对的。而对原因的解释常常在几周甚至几年后才显露出来。_Malcolm Gladwell_在他的[_Blink: The Power of Thinking Without Thinking_](https://www.amazon.com/Blink-Power-Thinking-Without/dp/0316010669/ref=sr_1_1?s=books&ie=UTF8&qid=1369995752&sr=1-1&keywords=blink)一书中曾探讨过这个问题。一个特定领域的专家常常能瞬间感知出一些事情是否正确,但却不能解释为什么。 +我能理解这种感觉,一种先行于逻辑的内心感性的感觉。就像我知道一件事是对的,却并不知道是如何和为什么我知道这是对的。而对原因的解释常常在几周甚至几年后才冒出来。_Malcolm Gladwell_在他的[_Blink: The Power of Thinking Without Thinking_](https://www.amazon.com/Blink-Power-Thinking-Without/dp/0316010669/ref=sr_1_1?s=books&ie=UTF8&qid=1369995752&sr=1-1&keywords=blink)一书中曾探讨过这个问题。一个特定领域的专家常常能瞬间感知出一些事情是否正确,但却不能解释为什么。 但得知_Dave_与`Elixir`『看对眼』时,我很想知道为什么他会这样。 -无独有偶,_Simon St. Laurent_也出了本`Elixir`的书。_Simon_的[_Introducing Erlang_](http://www.amazon.com/Introducing-Erlang-Simon-St-Laurent/dp/1449331769)一书表现不俗,我和他还通过邮件沟通过几次,还有有些熟悉的。而且_Pragmatic Press_和_O'Reilly_出版社都在争着要出版`Elixir`,我知道在`Erlang VM`上有事要发生,而我自己还没注意到。毫无疑问我Out了! +无独有偶,_Simon St. Laurent_也出了本`Elixir`的书。_Simon_的[_Introducing Erlang_](http://www.amazon.com/Introducing-Erlang-Simon-St-Laurent/dp/1449331769)一书表现不俗,我和他还通过邮件沟通过几次,所以有些事已经在酝酿了。而_Pragmatic Press_和_O'Reilly_出版社都在争着要出版`Elixir`,我知道在`Erlang VM`上的事已经在发生了,而我自己还没注意到。毫无疑问我Out了! 我发封邮件给_Dave_和_Simon_,他们爽快地借给我了样书,现在可以开始阅读了……谢了二位…… -# 上周我下载了`Elixir`然后开始学习…… +# 上周我下载了`Elixir`然后开始学习 -没多久我觉得就上手了。确实是个好货!有趣的是`Erlang`和`Elixir`两者在在底层一样的,对于我来说『感觉』是一样的。事实上也确实这样,两者都会被编译成`EVM`(`Erlang Virtual Machine`)指令 —— 实际上`EVM`这个叫法之前没人用,都叫成`Beam`,但为了和`JVM`区分开,我觉得是时候开始用`EVM`这个叫法了。 +没多久我觉得就上手了。确实是个好货!有趣的是`Erlang`和`Elixir`两者在在底层一样的,对我来说『感觉』是一样的。事实上也确实这样,两者都会被编译成`EVM`(`Erlang Virtual Machine`)指令 —— 实际上`EVM`这个叫法之前没人用,都叫成`Beam`,但为了和`JVM`区分开,我觉得是时候开始用`EVM`这个叫法了。 -`Erlang`和`Elixir`为什么有相同的『语意』?这得从虚拟机底层谈起。垃圾回收行为,不共享并发机制,底层的错误处理和代码加载机制都是一致的。当然这些肯定都是一致的:他们都运行在相同的`VM`里。这也是`Scala`和`Akka`区别于`Erlang`的原因。`Scala`和`Akka`都运行在`JVM`之上,垃圾回收和代码加载机制从根本上就不一样。 +`Erlang`和`Elixir`为什么有相同的『语义』(`semantics`)?这得从虚拟机底层谈起。垃圾回收行为,不共享并发机制,底层的错误处理和代码加载机制都是一致的。当然这些肯定都是一致的:他们都运行在相同的`VM`里。这也是`Scala`和`Akka`区别于`Erlang`的原因。`Scala`和`Akka`都运行在`JVM`之上,垃圾回收和代码加载机制从根本上就不一样。 -你直接看到的`Elixir`是完全不同的上层语法,源自`Ruby`。看起来不那么『可怕』语法和很多附加的甜点。 +你直接看到的`Elixir`是完全不同的上层语法,源自`Ruby`。看起来不那么『可怕』语法和很多附加的语法糖。 -`Erlang`的语法源自`Prolog`,并受到`Smalltalk`、`CSP`和函数式编程的影响很大。`Elixir`则受到`Erlang`和`Ruby`的影响很大。从`Erlang`借鉴了模式匹配(`pattern matching`)、高阶函数(`higher order function`)以及整个进程(`process`)和任其崩溃的(`let it crash`)错误处理(`error handling`)机制。从`Ruby`借鉴了`sigil`和快捷语法(`shortcut syntax`)。当然也有自创的甜点,像`|>`管道操作符(`|> pipe operator`),让人想到`Prolog`的`DCG`和`Haskell`的`Monad`(尽管相比要简单不少,更类似于`Unix`的管道操作符),还有宏的引用和反引用操作符(`macro quote and unquote operator`,对应的是`Lisp`的反引号和逗号操作符)。 +`Erlang`的语法源自`Prolog`,并受到`Smalltalk`、`CSP`和函数式编程的很大影响。`Elixir`则受到`Erlang`和`Ruby`的很大影响。从`Erlang`借鉴了模式匹配(`pattern matching`)、高阶函数(`higher order function`)以及整个进程(`process`)和任其崩溃的(`let it crash`)错误处理(`error handling`)理念。从`Ruby`借鉴了`sigil`和快捷语法(`shortcut syntax`)。当然也有自创的语法糖,像`|>`管道操作符(`|> pipe operator`),让人想到`Prolog`的`DCG`和`Haskell`的`monad`(尽管相比要简单不少,更类似于`Unix`的管道操作符),还有宏的引用和反引用操作符(`macro quote and unquote operator`,对应的是`Lisp`的反引号和逗号操作符)。 > 【译注】: > @@ -106,26 +105,22 @@ IO.puts "...#{x}..." 对`x`求值后把`x`友好格式化的表示(`a pretty-printed representation`)插入到字符串中。但是只对简单形式的`x`可行。 -这点可以通过从`Elixir`调用`Erlang`的方式很简单就能解决掉。 +因为可以通过从`Elixir`调用`Erlang`的函数,这点很简单就能解决。 -`IO.puts "...#{pp(x)}..."`这样就总是可行的。我只是把`pp(x)`定义成 +`IO.puts "...#{pp(x)}..."`总是可行的。我只是把`pp(x)`定义成 ```ex -def pp(x) do - :io_lib.format("~p", [x]) - |> :lists.flatten - |> :erlang.list_to_binary -end +def pp(x) do :io_lib.format("~p", [x]) |> :lists.flatten |> :erlang.list_to_binary end ``` -用`Erlang`则表述成: +用`Erlang`则写成: ```erl pp(X) -> list_to_binary(lists_flatten(li_lib:format("~p"), [X]))) ``` -很『显然』这和`Elixir`的版本表述是一样。当然`Elixir`的写法要更容易阅读。上面用到的`|>`操作符意思是把`io_lib:format`的结果输入到`lists:flatten`,然后再到`list_to_binary`。就像好用的老家伙`Unix`的管道符`|`。 +很『显然』这和`Elixir`的版本等价的。当然`Elixir`的写法可读性更好。上面用到的`|>`操作符意思是把`io_lib:format`的结果输入到`lists:flatten`,然后再到`list_to_binary`。就像好用的老家伙`Unix`的管道符`|`。 `Elixir`打破了一些`Erlang`神圣信条 —— 在顺序结构中变量可重绑定(`re-bound`)。实际上这也是可以做到的,因为最终结果还是可以规范化成静态单赋值(`static-single-assignment`,`SSA`)的形式。尽管在顺序结构中这是可以的,但在循环结构中,一定肯定以及确定不要这么做。但这不是个问题,因为`Elixir`木有循环,只有递归。实际上`Elixir`不可能在循环中包含可变的变量(`mutable variables`),因为这样编译出来的东西在下层的`EVM`是支持不了的。顺序结构的`SSA`变量挺好的,`EVM`知道如何对其做优化。但在循环结构不行,所以`Elixir`没有这么做。关于这方面的优化甚至可以更往下挖到`LLVM`汇编器(`LLVM assembler`) —— 但又是另一个很长的故事先就此打住吧。 @@ -139,13 +134,13 @@ pp(X) -> 对于错误的设计,你完了。你成了2B,如果好设计比坏设计多,你可能被原谅。你想在以后干掉这些坏设计,却因为向后兼容性或者是有些SB已经用上所有那些坏设计写上了1T行代码,结果你是改不了了。 -而难以理解的部分才是真正无赖。你必须一而再再而三地解释,直到你吐血,可还是有些人永远不懂,你必须写上百邮件和数千文字来一遍又一遍地地解释这是什么意思以及为什么会如此。对于一个语言的设计者或作者来说,这是个痛苦的深渊。 +而难以理解的部分才是真正无赖。你必须一而再再而三地解释,直到你吐血,可还是有些人永远不懂,你必须写上百邮件和数千文字来一遍又一遍地解释这是什么意思以及为什么会如此。对于一个语言的设计者或作者来说,这是个痛苦的深渊。 -下面我要说到的几件事,我认为也会落入这三类情况中。 +下面我要说的几件事,我认为也会落入这三类情况中。 -在我开始前,我先要说,`Elixir`做对了灰常灰常多对的事情,而且远远多于错的。 +在开始前,我首先要指出的是,`Elixir`做了一大把正确的事,远远多于做错的。 -关于`Elixir`有利的是,要改正它的错误还不算晚。这只能在无数代码行被写下和众多程序员开始使用它之前才能做到 —— 所以解决这些问题的时日并不多了。 +关于`Elixir`有利的是,要改正错误还不算晚。但这只能在无数代码行被写下和众多程序员开始使用它之前才能做到 —— 所以留给解决这些问题的时间并不多了。 # 1. 在源文件中没有版本 @@ -159,7 +154,7 @@ pp(X) -> 所有源文件中加上语言的版本是必要的。为什么呢? -早期的`Erlang`没有列表推导(`list comprehension`)。如果我们对一个新版的`Erlang`模块用一个旧的`Erlang`编译器去编译。新版的代码含有列表推导,但旧编译器并不知道列表推导,所以旧编译器会认为这是一个语法错误。 +早期的`Erlang`没有列表推导(`list comprehension`)。如果我们对一个新版的`Erlang`模块用一个旧的`Erlang`编译器去编译。新版的代码含有列表推导,但旧编译器并不知道列表推导,所以旧编译器会认为这是个语法错。 如果 **版本3** `Erlang`编译器处理这样开始的文件: @@ -200,9 +195,9 @@ fac(N) when N > 0 -> N * fac(N-1). 不能直接复制到`shell`里运行,得到相同的结果。_Dave_问这是为什么,并说这样很傻。 -在`Lisp`等其它语言主中这做是没问题的。_Dave_说了『这很会让人很迷惑』类似这样的话 —— 他说的对并且这确实让人迷惑了。在论坛里关于此的问题肯定有成百上千条。 +在`Lisp`等其它语言主中这做是没问题的。_Dave_说过『这很会让人很迷惑』类似这样的话 —— 他说的对并且这确实让人迷惑了。在论坛里关于这个问题肯定有成百上千条。 -我解释这个问题已经无数遍了,从黑发解释到白发,我现在头发真白了就是因为这个原因。 +我已经解释了这个问题无数遍,从黑发解释到白发,我现在头发真白了真就是因为这个。 原因是`Erlang`的一个`bug`。 @@ -268,7 +263,7 @@ iex> f.(10) 在学校里我学会了写**_`f(10)`_**来调用函数而不是**_`f.(10)`_** —— 这是个『真正』的函数,函数名是**`Shell.f(10)`**(一个在`shell`中定义的函数)。`shell`部分是**_隐式_**的,所以可以只用**_`f(10)`_**来调用。 -如果这点你置之不理,那就等着用你生命的接下来的二十年去解释为什么吧。等着在数百论坛里的数千封邮件吧。 +如果你对这点置之不理,那就等着用你生命接下来的二十年去解释为什么吧。等着在数百论坛里的数千封邮件吧。 # 4. 发送操作符 @@ -278,25 +273,25 @@ Process <- Message 这是啥玩意?你知道从`occam-pi`转成`Elixir`有多难么。 -这点让你现在在失去`occam-pi`社区路上。发送操作符就应该是**`!`**,像这样: +这点让你现在在失去`occam-pi`社区路上。发送操作符就应该是**_`!`_**,像这样: ```erl Process ! Message ``` -接下来的一周,我的大脑会变成浆糊,我的神经网络要被重编程,这样我才能『看到』`<-`时才能反应成`!` —— 这点不是在说如何我思考,而是指要重编程我更深植在脊柱里无意识反应。发送操作符已经不在我大脑里,而是在我的脊柱里。我的大脑想着『发送一个消息给一个进程』并发送信号给我的手指,我的脊柱马上加上**`!`**,接着大脑要**回退删除**这个字符改成`<-`。 +接下来的一周,我的大脑会变成浆糊,我的神经网络要被重编程,这样我才能『看到』`<-`时才能反应成**_`!`_** —— 这点不是在说如何我思考,而是指要重编程我更深植在脊髓里无意识反应。发送操作符已经不在我大脑里,而是在我的脊髓里。我的大脑想着『发送一个消息给一个进程』并发送信号给我的手指,我的脊髓马上加上**_`!`_**,接着大脑要**回退删除**这个字符改成**_`<-`_**。 -这是一个语法问题。让人爱恨交织的语法。如果10分制的评级标准,10代表『非常非常烂』,1代表『好吧,我可以适应』的话,这个问题我给3分。 +这是一个语法问题,而我们都喜欢对语法说长道短的。如果10分制的评级标准,10代表『非常非常烂』,1代表『好吧,我可以适应』的话,这个问题我给3分。 -这点会使`Occam-pi`的程序员很难转到`Elixir`,什么,只需要简单地使用**`!`**就能完成`<-`的功能?这可真是出人意料啊。相信会有很多人受到鼓舞的。 +这点会使`occam-pi`程序员很难转到`Elixir`。什么?只需要简单地用**`!`**而不是`<-`,就可以让一波`occam-pi`的程序员喜大普奔地哭喊着『药 !药!切克闹!!美好生活现在到!!!』然后立马就转到`Elixir`。以后老司机告诉你是这样的,这个改变会让人开心得像过节一样。 # 5. 管道运算符 -这是一个很好很好的东西并且很简单就能掌握,以至于没人会给你称赞。这就是生活。 +这就是之前我说的一个非常好非常好的想法,并且非常非常简单就能掌握,以至于没人会给你称赞。这就是生活。 -这是来自`Prolog`语言的隐性基因(`recessive gene`):`monad`。 在`Prolog`中的基因是显而易见的, 但是在`Erlang`中确实不明显的(`Prolog`的儿子)但是又在`Elixir`(`Prolog`的儿子的儿子)中重新显现了。(【译注】:隔代遗传) +这是来自`Prolog`语言的隐性基因(`recessive gene`):`monad`。 在`Prolog`中的基因是显而易见的, 但是在`Erlang`中确实不明显的(`Prolog`的儿子)但是又在`Elixir`(`Prolog`的儿子的儿子)中重新表现出来了。(【译注】:隔代遗传) -`x |> y`意味着调用了`x`然后获取了`x`的输出并且将它作为`y`的另外一个参数(第一个参数)。 +`x |> y*`意味着调用了`x`然后获取了`x`的输出并且将它作为`y`的另外一个参数(第一个参数)。 所以 @@ -311,7 +306,7 @@ newvar = x(1, 2); y(newvar, a, b, c); ``` -这**非常**有用。假设我们要把握的是把一个变量`abc`转换为`Abc`。在`Elixir`中没有利用的函数但是还有一个功能,就是去控制一个字符串。所以我们需要现将这个变量转换为`string`,在`Erlang`中,我们可以这样写: +这**非常**有用。假设我们要把一个原子(`atom`)转成首字母大写,即`abc`转换为`Abc`。在`Elixir`中没有对应的函数,但有把字符串转成首字母大写的函数。所以我们需要先将原子转换为字符串。用`Erlang`的实现代码如下: ```erl capitalize_atom(X) -> @@ -329,9 +324,9 @@ capitalize_atom(X) -> binary_to_atom(V4). ``` -但是,这更糟 —— 好恶心的代码。像这样德性的代码我都不知道写过多少次了!浪费我大把的青葱岁月。 +这更糟 —— 好恶心的代码。像这样德性的代码我都不知道写过多少次了!浪费我大把的青葱岁月。 -于是`|>`来了: +通过`|>`操作符,代码变更成了这样: ```ex X |> atom_to_list |> list_to_binary |> capitalize_binary @@ -402,15 +397,15 @@ B = s"Hello #{A}". %C{.....} ``` -`C`是单个字符(【译注】:`Erlang`中大写开头的是变量不是常量,`C`是单个字符,表示可以是`a`、`b`、`$`),后面跟着一对`{}`或`[]`。 +`C`是单个字符(【译注】:`Erlang`中大写开头的是变量不是常量,`C`是单个字符,表示可以是`a`、`b`、`$`等),后面跟着一对`{}`或`[]`。 -`sigil`很棒。`Erlang`本可以在15年前就有这个功能,而现在也可以引入,并且不会带来向后兼容的问题。 +`sigil`很棒。`Erlang`本可以在15年前就有这个功能,而现在也可以引入,并且不会有向后兼容的问题。 # 7. `docstring` 大爱`docstring`。 -但有个小意见。请把`docstring`放到函数定义**_里面_**。 +但有个小建议,请把`docstring`放到函数定义**_里面_**。 `Elixir`是这样: @@ -440,7 +435,7 @@ end # 8. `defmacro`引用 -爱之。在解析转换这个正确阶段所做的正确的事。这让可以让人舒舒服服得不用去知道抽象语法了。引用(`quote`)和反引用(`unquote`)为你把魔法都做好了。 +爱之。在解析转换这个正确阶段所做的正确的事。这可以让人舒舒服服的不用去知道抽象语法了。引用(`quote`)和反引用(`unquote`)为你把魔法都做好了。 这就是那种是对的事 —— 非常棒却真真儿难于解释。就像`Haskell`的`monad` —— 啊哈,`monad`真很容易解释,难怪有上千篇文章来解释它有多简单。 @@ -471,11 +466,11 @@ iex> lc x inlist [1, 2, 3], do : 2*x ** (SyntaxError) iex:2: invalid token: : 2*x ``` -哎呦~ 一定要是`do:`,`do :`不行。 +哎呦~ 一定要是`do:`,但`do :`不行。 -个人认为,空白符(`whitespace`)就是空白符。在字符串里面不能随便添加。在字符串外面,为了格式化代码我可以按自己喜好添加空白,这样可以让代码更美观。 +个人认为,空白符(`whitespace`)就是空白符。在字符串里面不能随便添加。在字符串外面,为了格式化代码我可以按自己喜好添加空白,好让代码更美观。 -但`Elixir`不能这么做 —— 不讨我喜欢。 +但在`Elixir`你不能这么做 —— 不讨我喜欢。 # 11. 闭包行为完全正确 —— 哦耶 @@ -496,9 +491,9 @@ js> f(10) 110 ``` -啥!函数`f`被打破了。定义的`f`,开始使用;修改了变量`a`有副作用打破了函数`f`。函数式编程的好处之一就是使程序变得容易推理。如果`f(10)`的值是15,那么就应该一直是15,不应该能在其它的地方打破。 +啥!函数`f`被打破了。定义的`f`,开始使用;修改了变量`a`有副作用打破了函数`f`。函数式编程的好处之一就是使程序变得易于推理。如果`f(10)`的值是15,那么就应该一直是15,不应该能在其它的地方被打破。 -`Elixir`呢? 闭包的处理是对的: +`Elixir`呢?正确地处理了闭包: ```ex iex> a = 5 @@ -513,7 +508,7 @@ iex> f.(10) 15 ``` -正确的闭包只应该包含不可变数据的指针 (`Erlang`中数据正是不可变的) —— 而不是可变数据的指针。如果闭包里有指向可变数据的指针,后面修改了数据就会破坏闭包的一致性。这样的结果就是不能把程序并行化,甚至顺序执行的代码也会诡异的错误。 +正确的闭包只应该包含不可变数据的指针 (`Erlang`中数据正是不可变的) —— 而不是可变数据的指针。如果闭包里有指向可变数据的指针,后面修改了数据就会破坏闭包的一致性。这样的结果就是不能把程序并行化,甚至对于顺序执行的代码也会引入诡异的错误。 在传统语言里要创建合适的闭包的代价会很高,因为捕获环境里的所有变量都需要做深拷贝,但`Erlang` 和`Elixir`不用这样,数据都是不可变的。你所要做的就是引用需要的数据。内部实现是通过指针引用数据(指针对程序员是不可见的),并且不再有指针引用的数据会被垃圾回收掉。 @@ -537,7 +532,7 @@ end 这个问题完全是可以解决的,我在`erlang2`语言实验并解决了。 -# 最后 +# 结束语 这就是我与`Elixir`的相处一周,非常兴奋的一周。 diff --git a/a-week-with-elixir/translation-postscript.md b/a-week-with-elixir/translation-postscript.md index 7810152..5ee97e5 100644 --- a/a-week-with-elixir/translation-postscript.md +++ b/a-week-with-elixir/translation-postscript.md @@ -11,7 +11,7 @@ 1. 翻译中有不少地方还需要改正和改进(当然是因为本文的主题比较难且作者涉及的计算机领域过多) - 约定俗成的技术术语翻译不准确 - 翻译不完整,如_Dave Thomas_在[_Programming Elixir_](http://pragprog.com/book/elixir/programming-elixir)对`Elixir`推荐。 -1. 分页随意杂乱 影响阅读,应该按文章结构分页 +1. 标点使用随意,中文标点使用成英文。 1. 没有目录 阅读不便 提供目录及按文章结构的分页,方便读者化整为零一节一节的阅读,进而更惬意读完整篇文章。 1. 关键技术术语没有附上英文术语,影响理解,也不方便自己进一步搜索查资料