Fork-join 创建自己的进程池来执行小粒度的任务。
相对于 Erlang 那种真正的抢占式调度的 VM 实现或者操作系统的抢占式调度,Fork-join 模型非常简单,也意味着相比之下效率相对低。
Fork-join 针对计算密集操作设计,意味着无法告诉 F/J 框架你因为 IO 等待而释放一会儿计算资源。所以,一般需要将异步 IO 操作放到另外的线程池,FJ 只处理纯计算。
基于 Scala 的 Akka 既是这种模型。所以,假如处理不当, Akka 的 Actor 很容易阻塞执行线程,如果执行线程池的线程被耗光,整个应用将会僵死在那里。而 Erlang 则没有这个问题。
VM 调度线程,将计算划分为非常小的执行单元。可以支持非常多的进程。IO 阻塞可以自动释放资源。真正的抢占式调度。
类型系统静态类型可以避免很多失误。动态类型经常会出现不可预期的结果,这有悖于 UNIX 风格的最少意外原则。
动态类型可以让开发更加快速。强静态类型系统会执行很快,比如 Java,但是也可以在有必要的时候使用反射,比如很多 RPC 框架的实现 (当然也有更进一步的字节码修改技术)。
每个语言的类型系统都有自己的特点。
最好能够区分 Interface、Struct 和 Implementation。能够以比较统一的模式轻松的定义自己需要的结构体。
GC 系统除非 Erlang 无可媲美的轻量级线程级别的 GC 。否则你要么需要记住和理解复杂的 GC 调优参数、要么像 PHP 那样过一段时间将进程杀掉重来。
元编程和 DSL 扩展性在语法级别的抽象和封装更能提高开发效率。Elixr 中如何实现 DSL。
执行速度和性能这点和并发并行模式、以及多核利用率密切相关。
UNIX 风格简单说就是模块化;每个模块完成相对单一的功能、复制任务由多个模块组合完成。项目设计就像搭积木。不同模块之前的输入输出可以拼接。
另外就是极简风格。
这点 Node.js npm 是最好的依赖管理系统了,这样导致了 Node.js 社群库数量的爆发。因为创建和发布一个库实在是太容易了,找到需要的库也非常简单。
极大提高了开发效率。
最好能打包成单一文件,容易分发和部署。比如 Java 应用打包成 Fat Jar 包到处执行,或者 Golang 那样编译成单一文件。
日志系统真实的项目、日志非常重要。之前的文章已经提到日志的重要性。所以好的内置日志系统或者比较统一高效的日志模式非常重要。
最好支持屏幕打印、写文件等等功能。这可能不能算一个编程语言的特性了,要看这个语言是不是有很好的日志类库。
Java 的 SLF 就是一个比较好的日志系统类库。
项目构建、编译、测试工具比较完善。
比如 Java、Scala 项目的 maven、sbt 。Erlang 项目可以用 rebar ,但是 Elixir 的 mix 友好的很多倍。
另外一个好的 REPL 命令行工具非常重要,因为这可以方便的侵入应用进行调试,或者测试一条代码片段。
比如 PHP 的 php -a, sbt, Clojure 的 lein, Erlang 的 erl, Elixir 的 iex 等等。
这是脚本语言的一大优势。小任务可以立刻创建一个脚本执行,而不需要修改、编译部署现有运行的应用。
这点对于小任务非常重要。Erlang 和 Elixir 都支持这样运行,escript 或者 Elixir 脚本。比如,连接到集群,读取状态或者进行一次性的数据操作,然后断开。
最好有一种比较标准的单元测试模型。比如 Java、Node.js、Scala、Elixir 等等。
说了这么多,回到 Elixir。首先 Elixir 执行和 Erlang 没有任何差别。Erlang 的优点 Elixir 完全具备。比如:真正的抢占式调度;充分利用多核心并行执行;Actor 模型;监控树;透明的分布式;
极其高的稳定性;代码的热更新部署;函数式编程;模式匹配;等等。并且很多 Erlang 下工具也是可以直接使用。比如 entop 。
另外 Elixir 比 Erlang 多出的好处在于更加友好的语法、工具链、社群。很多之前写 Ruby 的开始写 Elixir,因为他们的语法最接近。
Elixir 的元编程 (meta programming) 和 DSL1. quote 将代码变成 AST,很像 LISP 语法。
quote do: 1 + 22. 执行 quote 的表达式
Code.eval_quoted(quote do: 1 + 2)3. unquote 用来引用 quote 范围之外的变量
number = 13 Macro.to_string(quote do: 11 + unquote(number)) Elixir 成熟的工具链