diff --git a/README.md b/README.md index c6e154c..866f057 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ Chinese translations for classic IT resources. ------------------ 1. [`Erlang`之父学习`Elixir`语言的一周](a-week-with-elixir/README.md) -作为`Erlang`之父_Joe Armstrong_,对`Erlang VM`上的新语言`Elixir`做了很精彩评论和思考。在『特定领域专家的专业直觉』、『编程语言设计的三定律』、『管道运算符避免恶心代码』、『`Elixir`的`sigil`引出的程序语言如何定义/解释字符串』等等这些问题的深入广博的讨论及其个性鲜明又幽默诙谐的行文风格,都能强烈感受到_Joe Armstrong_的老黑客风范。 +作为`Erlang`之父_Joe Armstrong_,对`Erlang VM`上的新语言`Elixir`做了很精彩的评论和思考。『特定领域专家的专业直觉』、『编程语言设计的三定律』、『管道运算符避免恶心代码』、『`Elixir`的`sigil`引出的程序语言如何定义/解释字符串』等等问题的讨论,个性鲜明又幽默诙谐的行文风格,都能强烈感受到_Joe Armstrong_深入广博的老黑客风范。 `Lisp` ------------------ diff --git a/a-week-with-elixir/README.md b/a-week-with-elixir/README.md index 5ade6a7..4720c9e 100644 --- a/a-week-with-elixir/README.md +++ b/a-week-with-elixir/README.md @@ -6,9 +6,9 @@ ## 译序 -作为`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))! +[自己](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)。 ------------------------ @@ -31,7 +31,7 @@ PS:为什么要整理和审校翻译 参见 [译跋](translation-postscript.md - [5. 管道运算符](#5-%E7%AE%A1%E9%81%93%E8%BF%90%E7%AE%97%E7%AC%A6) - [6. `Elixir`有`sigil`](#6-elixir%E6%9C%89sigil) - [7. `docstring`](#7-docstring) -- [8. `defmacro`引用](#8-defmacro%E5%BC%95%E7%94%A8) +- [8. `defmacro`的引用和反引用](#8-defmacro%E7%9A%84%E5%BC%95%E7%94%A8%E5%92%8C%E5%8F%8D%E5%BC%95%E7%94%A8) - [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) @@ -45,7 +45,7 @@ PS:为什么要整理和审校翻译 参见 [译跋](translation-postscript.md 差不多一周前我开始看[`Elixir`](http://elixir-lang.org),关于`Elixir`之前只有些模糊的了解,没打算花时间去看细节。 -但在得知_Dave Thomas_出版了[《_Programming Elixir_》](http://pragprog.com/book/elixir/programming-elixir)这本书的消息后,我的想法就彻底改变了。_Dave Thomas_帮我修订过我的那本`Erlang`的书并且作为`Ruby`的倡导者做得非常出色,所以要是_Dave_对一样东西产生了兴趣,那说明这样东西的有趣性是毫无疑问的。 +但得知_Dave Thomas_出版了[《_Programming Elixir_》](http://pragprog.com/book/elixir/programming-elixir)这本书的消息后,我的想法彻底变了。_Dave Thomas_帮我修订过那本`Erlang`的书并且作为`Ruby`的倡导者做得非常出色,所以_Dave_要是对一样东西产生了兴趣,那说明这样东西的有趣性是毫无疑问的。 _Dave_对`Elixir`很感兴趣,在他的书里这样写道: @@ -57,12 +57,12 @@ _Dave_对`Elixir`很感兴趣,在他的书里这样写道: > 回首已经逝去15年的时光,而我无时无刻不在寻找下一个也能给出这样感觉的新『对象』。 > > 很快我遇上了`Elixir`,由于一些原因,我没能一见钟情。 -> 但在几个月前,和_Corey Haines_聊了一次,在如何不用那些学院派的书给大家介绍哪些有吸引力的函数式编程概念这个问题上诉了些苦。 +> 但在几个月前,和_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)一书中曾探讨过这个问题。一个特定领域的专家常常能瞬间感知出一些事情是否正确,但却不能解释为什么。 -但得知_Dave_与`Elixir`『看对眼』时,我很想知道为什么他会这样。 +但得知_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了! @@ -70,9 +70,9 @@ _Dave_对`Elixir`很感兴趣,在他的书里这样写道: # 上周我下载了`Elixir`然后开始学习 -没多久我觉得就上手了。确实是个好货!有趣的是`Erlang`和`Elixir`两者在在底层一样的,对我来说『感觉』是一样的。事实上也确实这样,两者都会被编译成`EVM`(`Erlang Virtual Machine`)指令 —— 实际上`EVM`这个叫法之前没人用,都叫成`Beam`,但为了和`JVM`区分开,我觉得是时候开始用`EVM`这个叫法了。 +没多久我觉得就上手了。确实是个好货!有趣的是`Erlang`和`Elixir`两者在底层一样的,对我来说『感觉』是一样的。事实上也确实如此,两者都会被编译成`EVM`(`Erlang Virtual Machine`)指令 —— 实际上目前`EVM`这个叫法没人用,都叫成`Beam`,但为了和`JVM`区分开,我觉得是时候开始用`EVM`这个叫法了。 -`Erlang`和`Elixir`为什么有相同的『语义』(`semantics`)?这得从虚拟机底层谈起。垃圾回收行为,不共享并发机制,底层的错误处理和代码加载机制都是一致的。当然这些肯定都是一致的:他们都运行在相同的`VM`里。这也是`Scala`和`Akka`区别于`Erlang`的原因。`Scala`和`Akka`都运行在`JVM`之上,垃圾回收和代码加载机制从根本上就不一样。 +`Erlang`和`Elixir`为什么有相同的『语义』(`semantics`)?这得从虚拟机底层谈起。垃圾回收行为,不共享并发模型,底层的错误处理和代码加载机制都是一致的。当然这是肯定的:他们都运行在相同的`VM`里。这也是`Scala`和`Akka`区别于`Erlang`的原因。`Scala`和`Akka`都运行在`JVM`之上,垃圾回收和代码加载机制从根本上就不一样。 你直接看到的`Elixir`是完全不同的上层语法,源自`Ruby`。看起来不那么『可怕』语法和很多附加的语法糖。 @@ -124,7 +124,7 @@ pp(X) -> 很『显然』这和`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`) —— 但又是另一个很长的故事先就此打住吧。 +`Elixir`打破了一些`Erlang`神圣信条 —— 在顺序结构中变量可重绑定(`re-bound`)。实际上这也是可以做到的,因为最终结果还是可以规范化成静态单赋值(`static-single-assignment`,`SSA`)的形式。尽管在顺序结构中这是可以的,但在循环结构中,一定肯定以及确定不要这么做。但这不是个问题,因为`Elixir`木有循环,只有递归。实际上`Elixir`不可能在循环中包含可变的变量(`mutable variables`),因为这样编译的输出在下层的`EVM`是支持不了的。顺序结构的`SSA`变量挺好的,`EVM`知道如何对其做优化。但在循环结构不行,所以`Elixir`没有这么做。关于这方面的优化甚至可以更往下挖到`LLVM`汇编器(`LLVM assembler`) —— 但又是另一个很长的故事先就此打住吧。 # 0. 编程语言设计的三定律 @@ -156,7 +156,7 @@ pp(X) -> 所有源文件中加上语言的版本是必要的。为什么呢? -早期的`Erlang`没有列表推导(`list comprehension`)。如果我们对一个新版的`Erlang`模块用一个旧的`Erlang`编译器去编译。新版的代码含有列表推导,但旧编译器并不知道列表推导,所以旧编译器会认为这是个语法错。 +早期的`Erlang`没有列表推导(`list comprehension`)。如果我们对一个新版的`Erlang`模块用老版的`Erlang`编译器去编译。新版的代码含有列表推导,但老编译器并不知道列表推导,所以旧编译器会认为这是个语法错。 如果 **版本3** `Erlang`编译器处理这样开始的文件: @@ -172,11 +172,11 @@ pp(X) -> > > 你刚刚给我一个版本5的程序,这说明我在地球上的寿命已过。 > -> 你将不得不杀掉我,把我卸载,然后安个版本5的新编译器。曾经玉树临风的我现在没了价值,我将不再存在。 +> 你将不得不杀掉我,卸载掉我,然后安个版本5的新编译器。曾经玉树临风的我现在没了价值,我将不再存在。 > > 再见吧,老朋友。 > -> 我感觉头痛。我要休息一下…… +> 我觉得头痛。我要休息一下…… 这是数据设计的第一法则: @@ -186,7 +186,7 @@ pp(X) -> # 2. `fun`和`def`不同 -在写[《_Programming Erlang_》](https://pragprog.com/book/jaerlang2/programming-erlang)一书时_Dave Thomas_问函数为什么不能输入到`shell`里。 +在写[《_Programming Erlang_》](https://pragprog.com/book/jaerlang2/programming-erlang)一书时_Dave Thomas_问函数为什么不能在`shell`里输入。 如果模块里有这样的代码: @@ -197,11 +197,11 @@ fac(N) when N > 0 -> N * fac(N-1). 不能直接复制到`shell`里运行,得到相同的结果。_Dave_问这是为什么,并说这样很傻。 -在`Lisp`等其它语言主中这做是没问题的。_Dave_说过『这很会让人很迷惑』类似这样的话 —— 他说的对并且这确实让人迷惑了。在论坛里关于这个问题肯定有成百上千条。 +`Lisp`等其它语言这么做是没问题的。_Dave_说过『这很会让人很迷惑』类似这样的话 —— 他说的对并且这确实让人迷惑了。在论坛里关于这个问题肯定有成百上千条。 我已经解释了这个问题无数遍,从黑发解释到白发,我现在头发真白了真就是因为这个。 -原因是`Erlang`的一个`bug`。 +之所以这样是因为`Erlang`的一个`bug`: - `Erlang`的模块是一系列的 **`FORM`** 。 - `Erlang` `shell`解析的是一系列 **`EXPRESSION`** 。 @@ -223,7 +223,7 @@ def triple(x) do end ``` -估计很多人都会直接从编辑器复制到`shell`里直接运行,然后收到的是出错信息: +估计很多人都会从编辑器复制到`shell`里直接运行,结果收到下面的出错信息: ```ex ex> def triple(x) do 3*x; end @@ -265,7 +265,7 @@ iex> f.(10) 在学校里我学会了写**_`f(10)`_**来调用函数而不是**_`f.(10)`_** —— 这是个『真正』的函数,函数名是**`Shell.f(10)`**(一个在`shell`中定义的函数)。`shell`部分是**_隐式_**的,所以可以只用**_`f(10)`_**来调用。 -如果你对这点置之不理,那就等着用你生命接下来的二十年去解释为什么吧。等着在数百论坛里的数千封邮件吧。 +如果你对这点置之不理,那就等着用你生命接下来的二十年去解释为什么吧。等着在数百个论坛里的数千封邮件吧。 # 4. 发送操作符 @@ -281,7 +281,7 @@ Process <- Message Process ! Message ``` -接下来的一周,我的大脑会变成浆糊,我的神经网络要被重编程,这样我才能『看到』`<-`时才能反应成**_`!`_** —— 这点不是在说如何我思考,而是指要重编程我更深植在脊髓里无意识反应。发送操作符已经不在我大脑里,而是在我的脊髓里。我的大脑想着『发送一个消息给一个进程』并发送信号给我的手指,我的脊髓马上加上**_`!`_**,接着大脑要**回退删除**这个字符改成**_`<-`_**。 +接下来的一周,我的脑子会变成浆糊,我的神经网络要被重新编程,这样我才能在『看到』`<-`时才能反应成**_`!`_** —— 这点不是在说如何我思考,而是指要重编程我更深植在脊髓里无意识反应。发送操作符已经不在我大脑里,而是在我的脊髓里。我的大脑想着『发送一个消息给一个进程』并发送信号给我的手指,我的脊髓马上加上**_`!`_**,接着大脑要**回退删除**这个字符改成**_`<-`_**。 这是一个语法问题,而我们都喜欢对语法说长道短的。如果10分制的评级标准,10代表『非常非常烂』,1代表『好吧,我可以适应』的话,这个问题我给3分。 @@ -431,17 +431,17 @@ def foo do end ``` -否则成了『没有归属的注释』(`detached comment`):当你编辑程序时,就可能出这样的问题。注释会与它要注释函数脱离开。 +否则成了『没有归属的注释』(`detached comment`):当你编辑程序时,就可能出这样的问题。注释会与它要去注释的函数脱离开。 在`Erlang`里,没有办法确定注释的是下一个函数还是上一个函数,或是模块。如果注释的对象是函数那就应该放到函数**_里面_**而不是外面。 -# 8. `defmacro`引用 +# 8. `defmacro`的引用和反引用 爱之。在解析转换这个正确阶段所做的正确的事。这可以让人舒舒服服的不用去知道抽象语法了。引用(`quote`)和反引用(`unquote`)为你把魔法都做好了。 这就是那种是对的事 —— 非常棒却真真儿难于解释。就像`Haskell`的`monad` —— 啊哈,`monad`真很容易解释,难怪有上千篇文章来解释它有多简单。 -`Elixir`宏真是简单 —— 引用(`quote`)对应`Lisp`的反引号(`quasiquote`),反引用(`unquote`)对应`Lisp`的列表逗号操作符(`list comma operator`) —— 这就我说的简单 :-) +`Elixir`的宏真是简单 —— 引用(`quote`)对应`Lisp`的反引号(`quasiquote`),反引用(`unquote`)对应`Lisp`的列表逗号操作符(`list comma operator`) —— 这就我说的简单 :-) # 9. 额外的符号 @@ -493,7 +493,7 @@ js> f(10) 110 ``` -啥!函数`f`被打破了。定义的`f`,开始使用;修改了变量`a`有副作用打破了函数`f`。函数式编程的好处之一就是使程序变得易于推理。如果`f(10)`的值是15,那么就应该一直是15,不应该能在其它的地方被打破。 +啥!函数`f`被打破了。定义`f`,开始使用;修改了变量`a`有副作用,打破了函数`f`。函数式编程的好处之一就是使程序变得易于推理。如果`f(10)`的值是15,那么就应该一直是15,不应该能在其它的地方被打破。 `Elixir`呢?正确地处理了闭包: @@ -540,7 +540,7 @@ end `Elixir`没有令人生畏的语法,融合了`Ruby`和`Erlang`优秀的特性。它不是`Erlang`也不是`Ruby`,有自己创新的想法。 -这是门新兴的语言,但在语言的开发的同时介绍的书也同步在写了。第一本介绍`Erlang` 的书在`Erlang`被发明后7年才出现,而畅销书更是在14年后才出现。用21年的时间去等一本真正的介绍书籍实在是太长了。 +这是门新兴的语言,但在语言的开发的同时介绍的书也同步在写了。第一本介绍`Erlang` 的书在`Erlang`发明后的7年才出现,而畅销书更是在14年后才出现。用21年的时间去等一本真正的介绍书籍实在是太长了。 _Dave_很喜欢`Elixir`,我也觉得很酷,我想我们会在使用过程中找到更多乐趣的。