update a-week-with-elixir

This commit is contained in:
Jerry Lee
2016-10-09 01:11:58 +08:00
parent fde3a04963
commit 2268cdf4d8

View File

@@ -1,4 +1,5 @@
原文链接:[A Week with `Elixir`](http://joearms.github.io/2013/05/31/a-week-with-elixir.html)[_Joe Armstrong_](http://joearms.github.io/)2013-05-31
基于开源中国社区的译文稿: [用`Elixir`的一周](https://www.oschina.net/translate/a-week-with-elixir)
------------------------
@@ -12,18 +13,18 @@
- [与`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)
- [编程语言设计的三定律](#%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)
- [在源文件中没有版本](#%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)
- [`funs`和`defs`不同](#funs%E5%92%8Cdefs%E4%B8%8D%E5%90%8C)
- [函数名称中有额外的](#%E5%87%BD%E6%95%B0%E5%90%8D%E7%A7%B0%E4%B8%AD%E6%9C%89%E9%A2%9D%E5%A4%96%E7%9A%84%E5%8F%A5%E7%82%B9)
- [发送操作符](#%E5%8F%91%E9%80%81%E6%93%8D%E4%BD%9C%E7%AC%A6)
- [管道运算符](#%E7%AE%A1%E9%81%93%E8%BF%90%E7%AE%97%E7%AC%A6)
- [`Elixir`有`Sigils`](#elixir%E8%BF%98%E6%9C%89sigils)
- [`Docstrings`](#docstrings)
- [defmacro 引用](#defmacro-%E5%BC%95%E7%94%A8)
- [强调符](#%E5%BC%BA%E8%B0%83%E7%AC%A6)
- [空格符](#%E7%A9%BA%E6%A0%BC%E7%AC%A6)
- [Closures完全一样](#closures%E5%AE%8C%E5%85%A8%E4%B8%80%E6%A0%B7)
- [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)
- [3. 函数名称中有额外的点](#3-%E5%87%BD%E6%95%B0%E5%90%8D%E7%A7%B0%E4%B8%AD%E6%9C%89%E4%B8%AA%E9%A2%9D%E5%A4%96%E7%9A%84%E7%82%B9%E5%8F%B7)
- [4. 发送操作符](#4-%E5%8F%91%E9%80%81%E6%93%8D%E4%BD%9C%E7%AC%A6)
- [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)
- [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)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -36,41 +37,57 @@
但在得知_Dave Thomas_出版了[_Programming Elixir_](http://pragprog.com/book/elixir/programming-elixir)这本书的消息后我的想法就彻底改变了。_Dave Thomas_帮我修订过我的那本`Erlang`的书并且作为`Ruby`的倡导者做得非常出色所以要是_Dave_对一样东西产生了兴趣那说明这样东西的有趣性是毫无疑问的。
_Dave_对`Elixir`很感兴趣, 在他的书里这样写道:
_Dave_对`Elixir`很感兴趣在他的书里这样写道
> 在1998年的时候由于我是`comp.lang.misc`邮件组的忠实读者,机缘巧合得知了`Ruby`,然后下载、编译、与`Ruby`坠入爱河。
> (没听过`comp.lang.misc`?那去问问你老爹吧。)
> 就像任何一次相爱经历一样,你很难解释是什么原因
> `Ruby`的工作方式和我想的灵犀默契,而且总是有足够的深度持续点燃着我的热情。
> 就像任何一次相爱经历一样,你很难解释原因是什么。
> `Ruby`的工作方式和我心里想的灵犀默契,而且总是有足够的深度持续点燃着我的热情。
>
> 一眨眼过去的15年里我无时无刻不在寻找一个也能给出这样感觉的新『对象』。
> 回首已经逝去15年的时光我无时无刻不在寻找一个也能给出这样感觉的新『对象』。
>
> 在不久前我遇上了`Elixir`由于一些原因,我完全没能沉浸其中
> 但在几个月前和_Corey Haines_聊了一次在如何不用学院派的书给大家介绍哪些有吸引力的函数式编程概念这个问题上诉了些苦。
> 他告诉我再去看看`Elixir`。我照做了,然后我有了第一次看到`Ruby`时相同的感觉。
> 很快我遇上了`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`的关注,我很想知道为什么他会这样。
但得知_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了
我给_Dave_和_Simon_发了封邮件,之后他们爽快地借给我了样书,现在可以开始阅读了……谢了……
发封邮件给_Dave_和_Simon_他们爽快地借给我了样书,现在可以开始阅读了……谢了二位……
# 上周我下载了`Elixir`然后开始学习……
没多久我觉得就上手了。确实是个好货!有趣的是`Erlang``Elixir`两者在在底层一样的,对于我来说『感觉』是一样的。事实上也确实这样,两者都会被编译成`EVM`(`Erlang Virtual Machine`)指令 —— 实际上`EVM`这个叫法之前没人用,都叫成`Beam`,但为了和`JVM`区分开,我觉得我们应该开始用`EVM`这个<C-D-Z>叫法了。
没多久我觉得就上手了。确实是个好货!有趣的是`Erlang``Elixir`两者在在底层一样的,对于我来说『感觉』是一样的。事实上也确实这样,两者都会被编译成`EVM`(`Erlang Virtual Machine`)指令 —— 实际上`EVM`这个叫法之前没人用,都叫成`Beam`,但为了和`JVM`区分开,我觉得是时候开始用`EVM`这个叫法了。
`Erlang``Elixir`为什么有相同的『语意』?这得从虚拟机底层谈起。垃圾回收行为,不共享并发机制,底层的错误处理和代码加载机制都是一致的。当然这些肯定都是一致的:他们都运行在相同的`VM`里。这也是`Scala``Akka`区别于`Erlang`的原因。`Scala``Akka`都运行在`JVM`之上,垃圾回收和代码加载机制从根本上就不一样。
你直接看到的`Elixir`是完全不同的上层语法,源自`Ruby`。看起来不那么『可怕』语法和很多附加的甜点。
`Erlang`的语法源自`Prolog`,并受到`Smalltalk``CSP`和函数式编程的影响很大。`Elixir`则受到`Erlang``Ruby`的影响很大。从`Erlang`借鉴了模式匹配(`pattern matching`)、高阶函数(`higher order functions`)以及整个进程(`process`)和任其崩溃的(`let it crash`)错误处理(`error handling`)机制。从`Ruby`借鉴了`Sigils`和快捷语法(`shortcut syntaxes`)。当然也有自创的甜点,像`|>`管道操作(`|> pipe operator`),让人想到`Prologs``DCGs``Haskell``Monads`(尽管相比要简单不少,更类似于`Unix`的管道操作),还有宏的引用和反引用操作符(`macro quote and unquote operators`,对应的是`Lisp`的反引号和逗号操作符)。
`Erlang`的语法源自`Prolog`,并受到`Smalltalk``CSP`和函数式编程的影响很大。`Elixir`则受到`Erlang``Ruby`的影响很大。从`Erlang`借鉴了模式匹配(`pattern matching`)、高阶函数(`higher order function`)以及整个进程(`process`)和任其崩溃的(`let it crash`)错误处理(`error handling`)机制。从`Ruby`借鉴了`sigil`和快捷语法(`shortcut syntaxe`)。当然也有自创的甜点,像`|>`管道操作`|> pipe operator`),让人想到`Prolog``DCG``Haskell``Monad`(尽管相比要简单不少,更类似于`Unix`的管道操作),还有宏的引用和反引用操作符(`macro quote and unquote operator`,对应的是`Lisp`的反引号和逗号操作符)。
> 【译注】:`Sigils`是指在变量名中包含符号来表达数据类型或作用域,通常作为前缀,如`$foo`,其中`$`就是个`Sigil`。
> 详见[`wikipedia`词条`Sigils`](https://en.wikipedia.org/wiki/Sigil_(computer_programming))
> 【译注】:
>
> **_`sigil`_**是指在变量名中包含符号来表达数据类型或作用域,通常作为前缀,如`$foo`,其中`$`就是个`sigil`。
> 像本文中说的例子,`sigil`也可以能对常量加上字母符号,`r"abc"`,其中`r`是`sigil`,把字符串转成正则表达式。
> 详见[`wikipedia`词条`sigil`](https://en.wikipedia.org/wiki/Sigil_(computer_programming))
>
> ---------------
>
> **_`DCG`_**`definite clause grammar`),确定性子句语法,表达语法的一种方式,可以用于自然语言或是形式化语言,比如像`Prolog`这样逻辑编程语言。基本的`DCG`用于描述『是什么』和『有什么特性』(简单的可以认为逻辑编程程序员要做的就是给出这些描述;剩下的事是逻辑引擎会根据描述的规则生成算法,然后得出解来)。像这样:
> ```prolog
> sentence --> noun_phrase, verb_phrase.
> noun_phrase --> det, noun.
> ```
> 不展开说明了,对于没有了解过`Prolog`/逻辑编程的同学意会一下就好,不用纠结了。详见[`wikipedia`词条`Definite clause grammar`](https://en.wikipedia.org/wiki/Definite_clause_grammar)
>
> ---------------
>
> 译文使用英文术语本身,不翻译成中文,有更好的辨识度。
`Elixir`还提供一个新的下层`AST`,取代了每个`Form`都是独有表示的`Erlang AST``Elixir AST`有一个统一得多的表示,这使得元编程(`meta-programming`)要简单得多。
`Elixir`还提供一个新的下层`AST`,取代了每个`form`都是独有表示的`Erlang AST``Elixir AST`有一个统一得多的表示,这使得元编程(`meta-programming`)要简单得多。
`Elixir`的实现出奇的可靠,尽管有几个地方和我预想的不一样。字符串插值(`string interpolation`)的工作方式有时候不好使(字符串插值是个很棒的想法)
@@ -101,17 +118,17 @@ 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. 编程语言设计的三定律
1. 你做对的,无人为你提。
1. 你做错的,有人跟你急。
1. 难于理解的,你必须一而再再而三地去给人解释。
一些语言有的设计做得太好,结果大家都懒得去提,这些好的设计是正确的、是优雅,是易于理解的。
一些语言有的设计做得太好,结果大家都懒得去提,这些好的设计是正确的、是优雅,是易于理解的。
对于错误的设计你完了。你成了2B如果好设计比坏设计多你可能被原谅。你想在以后干掉这些坏设计却因为向后兼容性或者是有些SB已经用上所有那些坏设计写上了1T行代码结果你改不了了。
对于错误的设计你完了。你成了2B如果好设计比坏设计多你可能被原谅。你想在以后干掉这些坏设计却因为向后兼容性或者是有些SB已经用上所有那些坏设计写上了1T行代码结果你改不了了。
而难以理解的部分才是真正无赖。你必须一而再再而三地解释,直到你吐血,可还是有些人永远不懂,你必须写上百邮件和数千文字来一遍又一遍地地解释这是什么意思以及为什么会如此。对于一个语言的设计者或作者来说,这是个痛苦的深渊。
@@ -121,7 +138,7 @@ pp(X) ->
关于`Elixir`有利的是,要改正它的错误还不算晚。这只能在无数代码行被写下和众多程序员开始使用它之前才能做到 —— 所以解决这些问题的时日并不多了。
# 在源文件中没有版本
# 1. 在源文件中没有版本
`XML`文件总是这样开始的:
@@ -133,12 +150,12 @@ pp(X) ->
所有源文件中加上语言的版本是必要的。为什么呢?
早期的`Erlang`没有列表推导(`list comprehensions`)。如果我们对一个新版的`Erlang`模块用一个旧的`Erlang`编译器去编译。新版的代码含有列表推导,但旧编译器并不知道列表推导,所以旧编译器会认为这是一个语法错误。
早期的`Erlang`没有列表推导(`list comprehension`)。如果我们对一个新版的`Erlang`模块用一个旧的`Erlang`编译器去编译。新版的代码含有列表推导,但旧编译器并不知道列表推导,所以旧编译器会认为这是一个语法错误。
如果 **版本3** `Erlang`编译器处理这样开始的文件:
如果 **版本3** `Erlang`编译器处理这样开始的文件
```erl
-version(5,0).
-version(5, 0).
```
则可以给出这样提示信息:
@@ -161,15 +178,15 @@ pp(X) ->
而 模块 **_是_** 数据。
# `funs`和`defs`不同
# 2. `fun`和`def`不同
在写_Programming Erlang_一书时_Dave Thomas_问函数为什么不能输入到`shell`里。
如果模块里有这样的代码:
```erl
fac(0) when N > 0 -> 1;
fac(N) -> N* fac(N-1).
fac(0) -> 1;
fac(N) when N > 0 -> N * fac(N-1).
```
不能直接复制到`shell`里运行得到相同的结果。_Dave_问这是为什么并说这样很傻。
@@ -180,9 +197,9 @@ fac(N) -> N* fac(N-1).
原因是`Erlang`的一个`bug`
- `Erlang`的模块是一系列的 **`FORMS`** 。
- `Erlang` `shell`解析的是一系列 **`EXPRESSIONS`** 。
-`Erlang``FORMS` 不是 **`EXPRESSIONS`** 。
- `Erlang`的模块是一系列的 **`FORM`** 。
- `Erlang` `shell`解析的是一系列 **`EXPRESSION`** 。
-`Erlang``FORM` 不是 **`EXPRESSION`** 。
```erl
double(X) -> 2*X. in an Erlang module is a FORM
@@ -209,15 +226,15 @@ ex> def triple(x) do 3*x; end
如果你不解决这个问题就要花后面20年的时间去解决为什么 —— 就像`Erlang`曾经所做的。
顺便说一下,修复这个问题真的真的很简单。我在`erl2`作为了尝试就解决了。`Erlang`中没法修复这个问题 (版本兼容问题),所以我就在[erl2](https://github.com/joearms/erl2)解决。只需要小改一下`erl_eval`和解析器的几个微调。
顺便说一下,修复这个问题真的真的很简单。我在`erl2`作为了尝试就解决了。`Erlang`中没法修复这个问题 (版本兼容问题),所以我就在[`erl2`](https://github.com/joearms/erl2)解决。只需要**_`erl_eval`_**的小改和解析器的几个微调。
主要原因是`FORMS`不是`expressions`, 所以加了个关键字`def`
主要原因是`FORM`不是`EXPRESSION`所以加了个关键字**`def`**。
```erl
Var = def fac(0) -> ; fac(N) -> N*fac(N-1) end.
Var = def fac(0) -> 1; fac(N) -> N*fac(N-1) end.
```
这就定义了一个有副作用的`expression`。由于是`expressions`,可以在`shell`中求值了,记`shell`中只能求值`expressions`
这就定义了一个有副作用的表达式。由于是个表达式,可以在`shell`中求值了,记`shell`中只能对表达式求值。
副作用指的是需要创建一个`shell:fac/1`功能(就像在模块中定义的一样)。
@@ -227,11 +244,11 @@ iex> double = fn(x) -> 2 * x end;
iex> def double(x) do 2*x end;
```
上面两者应该是一致的,并且**都是**定义一个名为`Shell.double`的函数。
上面两者应该是一致的,并且**都是**定义一个名为**`Shell.double`**的函数。
做了这样的修改,妈妈再也不用担心我会白头了。
# 函数名称中有额外的
# 3. 函数名称中有额外的点
```ex
iex> f = fn(x) -> 2 * x end
@@ -240,11 +257,11 @@ iex> f.(10)
20
```
在学校里我学会了写`f(10)`来调用函数而不是`f.(10)` —— 这是个『真正』的函数,函数名是`Shell.f(10)`(一个在`shell`中定义的函数)。`shell`部分是隐式的,所以可以只用`f(10)`来调用。
在学校里我学会了写**_`f(10)`_**来调用函数而不是**_`f.(10)`_** —— 这是个『真正』的函数,函数名是**`Shell.f(10)`**(一个在`shell`中定义的函数)。`shell`部分是**_隐式_**的,所以可以只用**_`f(10)`_**来调用。
如果这点你置之不理,那就等着用你生命的接下来的二十年去解释为什么吧。等着在数百论坛里的数千封邮件吧。
# 发送操作符
# 4. 发送操作符
```ex
Process <- Message
@@ -262,37 +279,37 @@ Process ! Message
这是一个语法问题。让人爱恨交织的语法。如果10分制的评级标准10代表『非常非常烂』1代表『好吧我可以适应』的话这个问题我给3分。
这点会使`Occam-pi`的程序员很难转到`Elixir`,什么,只需要简单地使用**`!`**就能完成`<-`的功能?这可真是出人意料啊。相信会有很多人受到鼓舞的
这点会使`Occam-pi`的程序员很难转到`Elixir`,什么,只需要简单地使用**`!`**就能完成`<-`的功能?这可真是出人意料啊。相信会有很多人受到鼓舞的
# 管道运算符
# 5. 管道运算符
这是一个很好很好的东西并且很简单就能掌握,以至于没人会给你称赞。这就是生活。
这是来自`Prolog`语言的基因。 在`Prolog`中的基因是显而易见的, 但是在`Erlang`中确实不明显的(`Prolog`的儿子)但是又在`Elixir``Prolog`的儿子的儿子)中重新显现了。
这是来自`Prolog`语言的隐性基因(`recessive gene``monad`。 在`Prolog`中的基因是显而易见的, 但是在`Erlang`中确实不明显的(`Prolog`的儿子)但是又在`Elixir``Prolog`的儿子的儿子)中重新显现了。(【译注】:隔代遗传)
`x |> y`意味着调用了`x`然后获取了`x`的输出并且将它作为`y`的另外一个参数(第一个参数)
`x |> y`意味着调用了`x`然后获取了`x`的输出并且将它作为`y`的另外一个参数第一个参数
所以
```ex
x(1,2) |> y(a,b,c)
x(1, 2) |> y(a, b, c)
```
意味着
等价于下面的代码:
```erl
newvar = x(1,2);
y(newvar,a,b,c);
newvar = x(1, 2);
y(newvar, a, b, c);
```
着非常有用。假设我们要把握的是把一个变量 abc 转换为 Abc’。 在Elixir中没有利用的函数但是还有一个功能就是去控制一个字符串。 所以我们需要现将这个变量转换为string,在Erlang中我们这样写
这**非常**有用。假设我们要把握的是把一个变量`abc`转换为`Abc`。在`Elixir`中没有利用的函数但是还有一个功能,就是去控制一个字符串。所以我们需要现将这个变量转换为`string`,在`Erlang`中,我们可以这样写
```erl
capitalize_atom(X) ->
list_to_atom(binary_to_list(capitalize_binary(list_to_binary(atom_to_list(X))))).
```
这样写太业余了,所以,所以我们还可能会写成这样
这样的写法太惊悚了。我们还可写成这样
```erl
capitalize_atom(X) ->
@@ -303,45 +320,46 @@ capitalize_atom(X) ->
binary_to_atom(V4).
```
但是,这更糟-恶心的代码。 我都不想再看到他了。
于是 |> 来了:
但是,这更糟 —— 好恶心的代码。像这样德性的代码我都不知道写过多少次了!浪费我大把的青葱岁月。
```ruby
于是`|>`来了:
```ex
X |> atom_to_list |> list_to_binary |> capitalize_binary
|> binary_to_list |> binary_to_atom
```
为什么我回调用这个隐藏的方法?
为什么我认为`|>`是隐性基因?
`Erlang``Prolog` 中演化而来, 而且 `Elixir` 也继承了 `Erlang`
`Erlang``Prolog`中演化而来而且`Elixir`也继承了`Erlang`
`Prolog``DCGs` ,所以
`Prolog``DCG`,所以
```prolog
foo --> a,b,c.
foo --> a, b, c.
```
得到了扩展成
扩展后的形式:
```prolog
foo(In, Out) :- a(In, V1), b(V1,V2), c(V2, Out).
foo(In, Out) :- a(In, V1), b(V1, V2), c(V2, Out).
```
这基本上是同样的想法。我们创建了一个额外的隐藏参数的方法调用线程的方式在函数调用序列的进出。这是一种典型的`Haskell`方式,但请保守这个秘密
这基本上是同样的想法。我们通过新加一个额外的隐藏参数把函数调用序列的输入输出串接起来了。这类似`Haskell``monad`用法,但做得很隐秘
`Prolog``DCGs`, `Erlang` 没有, `Elixir` 有管道操作符!
`Prolog``DCG``Erlang`没有`Elixir`有管道操作符!
# `Elixir`有`Sigils`
# 6. `Elixir`有`sigil`
`Sigils`好,我们要加到`Erlang`里。
`sigil`棒 —— 爱之。我们应该加到`Erlang`里。
字符串是一抽象。 编程语言通常使用双引号圈住它们。就像这样:
字符串是一个编程抽象。编程语言都有字符串常量,通常使用双引号包着的一串字符。就像这样的一行代码:
```erl
```c
x = "a string"
```
编译器在按照语意将其转换
编译器会转换成字符串的内部表示,关联上对应的语义
`Erlang`
@@ -349,55 +367,57 @@ x = "a string"
X = "abc"
```
代表 “X 是ASCII中a,b,c的整数表示”
表示『`X`是字符`a, b, c``ASCII`码值对应的整数的列表』。
但也可能自定义。`Elixir`里x = abc” 代表x 是 UTF8 字符集y。 通过在最前面加上r可以改变默认集`Erlang`里这样写:
但也可以选择成任何其它我们想要的含义。`Elixir``x = "abc"`代表`x`是一个`UTF8`编码二进制(`binary`)(【译注】:`binary``EVM`的内置类型)。通过在双引号前面加上`r`可以改变字符串含义成和`Erlang`一样:
```ex
X = r"...."
```
代表编译过的正则表达式, i。e.和 X = re:compile("…")一样 -将已知字符解释为想要的不同模式。下面代码:
当然也可以被定义成代表编译过的正则表达式,也就是说和等价于`X = re:compile("...")` ——
基于我们确定字符串的含义,可以以不同的方式去解释(`interpret`)内容。可以写上这样的代码:
```ex
A = "Joe",
B = s"Hello #{A}".
```
B值是“Hello Joe” - s代表&nbsp;“替换当中的字符”
`B`值可以是`Hello Joe` —— 这里`sigil` `s`改变字符串常量解释行为,『替换变量的值并插入』
`Elixir`在这方面做得很好,很多不同的`sigils`
`Elixir`在这方面做得很好,定义了很多不同的`sigil`
`Elixir``sigil`语法不太一样,如下:
```ruby
```ex
%C{.....}
```
在{}或 []接上C
`C`是单个字符(【译注】:`Erlang`中大写开头的是变量不是常量,`C`是单个字符,表示可以是`a``b``$`),后面跟着一对`{}``[]`
`Sigils`确实不错。 `Erlang` 15年前就有这个功能现在引入也不会有什么兼容问题。</div></td>
`sigil`很棒。`Erlang`本可以在15年前就有这个功能现在也可以引入,并且不会带来向后兼容问题。
# `Docstrings`
# 7. `docstring`
`Docstrings`真好
大爱`docstring`
但有个小建议。 `docstring`放到功能定义里
但有个小意见。请`docstring`放到函数定义**_里面_**
如此
`Elixir`是这样:
```python
```ex
@doc """
...
...
"""
def foo do
...
...
end
```
这般引入:
```python
放到函数**_里面_**会是这样:
```ex
def foo do
@doc """
...
@@ -405,54 +425,54 @@ def foo do
end
```
否则会出“detached comment”: 代码会出错。注释会在调用功能时断开。
否则成了『没有归属的注释』(`detached comment`):当你编辑程序时,就可能出这样的问题。注释会与它要注释函数脱离开。
Erlang里我没法分出注释是上一个功能还是下个功能的。 最好就是写到对应的功能模块内部
`Erlang`里,没有办法确定注释的是下一个函数还是上一个函数,或是模块。如果注释的对象是函数那就应该放到函数**_里面_**而不是外面
# defmacro 引用
# 8. `defmacro`引用
好东西。 这是解析模式的转换。 好处是无需关注其实现,引用帮你解决问题了。
爱之。在解析转换这个正确阶段所做的正确的事。这让可以让人舒舒服服得不用去知道抽象语法了。引用(`quote`)和反引用(`unquote`)为你把魔法都做好了。
这就是那些妙不可言的东西之一 - 需要解释一下。 就像`Haskell``monads` - Yup, `monads`确实很容易解释,难怪需要有那么多文章解释它有多好用(其实不简单)。
这就是那种是对的事 —— 非常棒却真真儿难于解释。就像`Haskell``monad` —— 啊哈,`monad`很容易解释,难怪有上千篇文章解释它有多简单。
`Elixir`简单 -就是`lisp``quasiquote`和comma operator - 简单吧)
`Elixir`真是简单 —— 引用(`quote`)对应`Lisp`反引号(`quasiquote`),反引用(`unquote`)对应`Lisp`的列表逗号操作符(`list comma operator` —— 这就我说的简单 :-)
# 强调符
# 9. 额外的符号
像这样:
像这样
```ruby
iex> lc x inlist [1,2,3], do: 2*x
[2,4,6]
```ex
iex> lc x inlist [1, 2, 3], do: 2*x
[2, 4, 6]
```
不是这样:
不是这样
```ruby
iex> lc x inlist [1,2,3] do: 2*x
```ex
iex> lc x inlist [1, 2, 3] do: 2*x
** (SyntaxError) iex:3: syntax error before: do
```
后面的冒号让人迷惑。
列表后面额外的冒号让人迷惑。
# 空格
# 10. 奇怪的空白
```python
iex> lc x inlist [1,2,3], do : 2*x
```ex
iex> lc x inlist [1, 2, 3], do : 2*x
** (SyntaxError) iex:2: invalid token: : 2*x
```
应该是“do:” 不是“do :”
哎呦~ 一定要是`do:``do :`不行。
空格就是空格。 字符串里面不能随便用,但除此之外即便为了美观我也会经常使用空格符
个人认为,空白符(`whitespace`)就是空白符。在字符串里面不能随便添加。在字符串外面,为了格式化代码我可以按自己喜好添加空白,这样可以让代码更美观
Elixir不行 - 让人不喜欢。
`Elixir`不能这么做 —— 不讨我喜欢。
# Closures完全一样
# 11. 闭包行为完全正确 —— 哦耶
`Elixir` (fns)`Closures``Erlang` (funs)一样。
`Elixir`闭包(`closure`)(即`fn`表达式)和`Erlang`完全一样。
`fn`能捕获在其功能范围能的任何变量值,这一点非常有用(ie 创建恒定 `closures`) 。 `JavaScript` 这块做的很不好。一个`JavaScript``Elixir`对比:
`fn`表达式有一个很好的特性:能捕获所在作用域的任何变量的当前值(换句话说:能创建不可变的闭包(`immutable closure`)),这点令人难以置信的有用。需要说一下,`JavaScript`在这点上非常错误。给一个`JavaScript``Elixir`例子,方便看到这点上的差异:
```js
js> a = 5;
@@ -467,12 +487,12 @@ js> f(10)
110
```
功能 f错了。 定义的f, 开始使用。 重定义f就不行了。函数编程的好处就是使编程变得简单。如果 f(10)的值是15就不应该在变来变去了
啥!函数`f`被打破了。定义的`f`,开始使用;修改了变量`a`有副作用打破了函数`f`。函数编程的好处之一就是使程序变得容易推理。如果`f(10)`的值是15那么就应该一直是15不应该能在其它的地方打破
`Elixir`呢? 没问题:
`Elixir`呢? 闭包的处理是对的:
```python
iex> a = 5
```ex
iex> a = 5
5
iex> f = fn(x) -> x + a end
#Function<erl_eval.6.17052888>
@@ -484,22 +504,20 @@ iex> f.(10)
15
```
正确的`closures`就应该指向恒定的数据地址 (就像`Erlang`) - 而不是数据的可变部分。 如果`closure`里有指向可变数据的指针,后面改了数据就会破坏`closure`的一致性。 死锁互斥问题。
正确的闭包只应该包含不可变数据的指针 (`Erlang`中数据正是不可变的) —— 而不是可变数据的指针。如果闭包里有指向可变数据的指针,后面改了数据就会破坏闭包的一致性。这样的结果就是不能把程序并行化,甚至顺序执行的代码也会诡异的错误。
一般使用`closure`的代价很高,需要复制环境里的所有变量深拷贝,但`Erlang``Elixir`这样, 数据都是恒定的。可以用的时候再去找。也是通过指针实现的(内部操作) 垃圾回收机制会移除所有没有指针关联的数据么,避免空指针
在传统语言里要创建合适的闭包的代价很高,因为捕获环境里的所有变量都需要做深拷贝,但`Erlang``Elixir`这样数据都是不可变的。你所要做的就是引用需要的数据。内部实现是通过指针引用数据(指针对程序员是不可见的),并且不再有指针引用的数据会被垃圾回收掉
`closures`可以写到 `shell`, 但不能写到模块里。
`shell`中可以有闭包,但不能写到模块里。
如果写成这样
`shell`里,如果可以这样
```ruby
```ex
a = 10;
f = fn(x) -> a + x end;
```
`shell`
为什么不能这样呢?
为什么不能在模块里这样写呢?
```erl
a = 10;
@@ -508,18 +526,18 @@ def f(x) do
end
```
`erlang2`是可行的
这个问题完全是可以解决的,我`erlang2`语言实验并解决了
# 最后
这是我开始`Elixir`第一周, 确实没让人失望
是我`Elixir`相处一周,非常兴奋的一周
`Elixir` 语法简洁并融合了`Ruby``Erlang`的长处。还有自己创新。
`Elixir`没有令人生畏的语法,融合了`Ruby``Erlang`优秀的特性。它不是`Erlang`也不是`Ruby`有自己创新的想法
这是门新的语言,但介绍的书却不是同步的。第一本介绍`Erlang` 的书在`Erlang`被发明后7年才出现,畅销书更是在14年后才出现。用21年去等一本真正的介绍书籍,代价太大
这是门新的语言,但在语言的开发的同时介绍的书也同步在写了。第一本介绍`Erlang` 的书在`Erlang`被发明后7年才出现,而畅销书更是在14年后才出现。用21年的时间去等一本真正的介绍书籍实在是太长了
Dave很喜欢`Elixir`,我也觉得很酷,我想我们会在使用过程中找到更多乐趣的。
_Dave_很喜欢`Elixir`,我也觉得很酷,我想我们会在使用过程中找到更多乐趣的。
`Erlang`在占世界二分之一的手机网络中提供了抢到的支持,像是`WhatsApp`。在简化版出现后,会有更多的新鲜血液加入这个阵营,我想以后的发展一定更加有意义
像是`WhatsApp`这个应用和全世界一半手机网络的关键部分都是搭建在`Erlang`之上。当技术变得更加亲和,当新一批热衷者进入阵营,让我现在怀着非常欣喜的心情关注着后续要发生的变化
这是篇即兴短文。也许会有些不妥之处,欢迎大家指正。
这是篇即兴的文章。也许会有些不妥之处,欢迎大家指正。