Learning
VOL. XIII · NO. 09 · Elixir · 01 JAN 1970

Elixir vs Go / Clojure / C / Java / Kotlin

Elixir 编程 · 01 JAN 1970 · 10 min read · 1,545 words
· · ·

一节课把 Elixir 跟 5 个主流语言的核心差异过一遍,速查表为主。

**预计时间:**30 分钟。

本课前提:你已经读过 0001-0003,对 Elixir 语法、并发、协议/行为/宏都有印象。这课是横向对照——你已知的语言在每个点上跟 Elixir 怎么对应。

一、总览:Elixir 在语言谱系里位置

维度Elixir最像最不像
范式函数式 + ActorClojure / HaskellC / Java
运行平台BEAM (Erlang VM)JVM (Clojure)Native (Go/Rust/C)
类型动态(可选 spec)Clojure / JSHaskell / Rust / Java
并发模型消息传递 (Actor)Akka / ErlangC thread / Java Thread
元编程宏(homoiconic)Clojure / RacketGo / Java
生态成熟度小但精(Web/Phoenix)Clojure(小)Java(巨大)

二、Elixir vs Go

两者都强调并发,但哲学不同:

维度Elixir / BEAMGo
并发单位进程(独立堆)goroutine(共享堆)
通信消息 send(pid, msg)channel ch <- msg
共享内存不允许(消息传递唯一)允许(但要 mutex 同步)
崩溃处理Supervisor 重启(“let it crash”)panic / recover,goroutine 死了就死
调度抢占式(reductions 计数)协作式(M:N)
类型动态静态(Go 1.18+ 有泛型)
部署mix release → 整目录(含 VM)单二进制(静态链接)
热升级原生支持不支持
元编程宏(强)无(go generate 凑合)
生态焦点Web / 消息 / 电信云原生 / CLI / 基础设施

代码对照:

# Elixir:异步计算
task = Task.async(fn -> heavy_calc(42) end)
result = Task.await(task)  # 阻塞等

# Go:异步计算
ch := make(chan int)
go func() { ch <- heavyCalc(42) }()
result := <-ch  # 阻塞等

# Elixir:消息循环
def loop do
  receive do
    :stop -> :ok
    msg -> handle(msg); loop()
  end
end

# Go:goroutine + select
for {
  select {
  case msg := <-ch: handle(msg)
  case <-stopCh: return
  }
}

何时选谁:

  • **Elixir 强:**长连接、聊天、IM、WebSocket、电信、Phoenix LiveView
  • **Go 强:**CLI 工具、K8s operator、单二进制部署、强类型

三、Elixir vs Clojure

最像的兄弟——同源于 Lisp 家族,都强调不可变 + 宏 + FP。

维度Elixir / BEAMClojure / JVM
运行平台BEAM(软实时,30 年验证)JVM(生态巨大,GC pause)
不可变数据结构基础:List/Tuple/Map(可变性看 GC)内置:Persistent Vector / HashMap / Tree
并发模型Actor / 进程消息core.async channel / 原子 / ref
支持(需卫生手动)支持(完全卫生)
类型动态动态(可选 spec)
启动时间~ 1 秒(VM 启 + 监督树)~ 3-10 秒(JVM 启)
宏卫生性手动(var!完全自动(syntax-quote
生态库小(Hex 30 万+ 包)巨大(任何 Java 库都能调)
状态共享消息 / ETSatom / ref / agent
主要应用Web (Phoenix) / 消息 / 嵌入式数据分析 / 异步 / 后端服务

代码对照:

# Elixir:管道
list
|> Enum.filter(&(&1 > 0))
|> Enum.map(&(&1 * 2))
|> Enum.sum()

# Clojure:threading macro
(->> list
  (filter pos?)
  (map #(* % 2))
  (reduce +))

# Elixir:defstruct
defmodule User do
  defstruct [:name, :age]
end

# Clojure:defrecord
(defrecord User [name age])

# Elixir:case
case result do
  {:ok, v} -> ...
  {:error, e} -> ...
end

# Clojure:case / match
(case result
  [:ok v] ...
  [:error e] ...)

何时选谁:

  • **Elixir 强:**需要 99.999% 可用性、长连接、软实时(电信/IM)
  • **Clojure 强:**需要 JVM 生态、数据分析、Spark/Storm 集成

四、Elixir vs C

完全不同的物种——C 是底层,Elixir 是高级抽象。

维度ElixirC
运行平台BEAM 字节码Native(编译到机器码)
内存模型不可变,进程独立堆裸指针,手动管理
类型动态(unlimited precision int)静态(int 32/64)
并发进程 / 消息 / 抢占调度pthread / mutex(要自己写)
性能比 C 慢 5-10x(GC 开销)最快
安全性无内存漏洞(无指针)buffer overflow 等
互操作通过 NIF 调 C调所有系统调用

代码对照(同样”求平方和”):

// C
int sum_of_squares(int arr[], int n) {
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += arr[i] * arr[i];
    }
    return sum;
}

# Elixir
def sum_of_squares(arr) do
  arr
  |> Enum.map(&(&1 * &1))
  |> Enum.sum()
end

何时选谁:

  • **Elixir 强:**业务逻辑、分布式系统、不可变数据流
  • **C 强:**操作系统、嵌入式、性能关键的库(BEAM 内部用 C 实现)

五、Elixir vs Java

JVM vs BEAM——都强调”平台 + 生态”,但运行时哲学不同。

维度Elixir / BEAMJava / JVM
并发单位BEAM 进程(~ 0.5 KB)Thread(~ 512 KB)
共享内存不允许堆共享,靠 synchronized
崩溃let it crash + supervisortry-catch + 重试
GC每进程独立(无 global pause)全堆 GC(G1/ZGC 有停顿)
类型动态(spec 可选)静态(编译期检查)
OOP无(用进程 + 消息)强制(class / interface)
部署mix releaseJAR + JVM
生态规模巨大
主要应用Web (Phoenix) / 消息 / 电信企业应用 / Android / 大数据

代码对照:

// Java
class UserService {
    public User getUser(Long id) throws UserNotFoundException {
        Optional<User> u = userRepo.findById(id);
        return u.orElseThrow(() -> new UserNotFoundException(id));
    }
}

// Elixir
defmodule UserService do
  def get_user(id) do
    case Repo.get(User, id) do
      nil -> {:error, :not_found}
      user -> {:ok, user}
    end
  end
end
关键直觉差异
Java 用 throw + try-catch 处理"预期错误",Elixir 用 {:ok, x} / {:error, reason}

Java 哲学:"异常是异常情况"。Elixir 哲学:"errors are values, not exceptions"。

六、Elixir vs Kotlin

Kotlin 是 JVM 上的”现代 Java”,加了协程。Elixir 在 BEAM 上原生做这些。

维度ElixirKotlin / JVM
范式纯函数式多范式(OOP + FP + 协程)
并发Process / 消息Coroutine / suspend
类型动态静态(强类型,可空)
不可变默认需要 val + data class + copy
空安全无(nil 自由)有(?. 强制处理)
平台BEAMJVM / Native / JS / Wasm

代码对照(Kotlin Coroutine vs Elixir Process):

// Kotlin
suspend fun fetchUser(id: Long): Result<User> = coroutineScope {
    val deferred = async { api.getUser(id) }
    val profile = async { api.getProfile(id) }
    Result.success(User(deferred.await(), profile.await()))
}

// Elixir
def fetch_user(id) do
  Task.async_stream([:api_get_user, :api_get_profile], fn endpoint ->
    apply(Pipeline.Api, endpoint, [id])
  end)
  |> Enum.map(fn {:ok, x} -> x end)
  |> case do
    [user, profile] -> {:ok, %{user: user, profile: profile}}
    _ -> {:error, :incomplete}
  end
end

七、6 个语言速记口诀

用一句话记住每个语言最适合什么:

语言一句话定位
Elixir消息 / 长连接 / 软实时,30 年电信验证
Go云原生 / 基础设施 / 单二进制部署
ClojureJVM 上的 FP / 数据分析 / 不可变优先
C操作系统 / 嵌入式 / 性能基准线
Java企业应用 / Android / JVM 生态
Kotlin现代 JVM / 空安全 / Coroutine

八、Elixir 选型决策树

需要同时支持多少用户?
├── < 1000 并发 → 任何语言都行,选你熟悉的
├── 1000-10000 → Go / Kotlin 够用
├── 10000-100000 → Go / Elixir 都能上
├── 100000+ → Elixir / Erlang 强项
└── 1000000+ → 几乎只能 BEAM / Rust + 异步 I/O

需要多少 9?
├── 99% → 任何语言
├── 99.9% → Go / Java / Kotlin
├── 99.99% → Java / Elixir
└── 99.999%(5 个 9)→ Elixir / Erlang

需要 软实时(几十 ms 内响应)?
├── 是 → Elixir / Erlang / C
└── 否 → 任何语言

需要 WebSocket / 长连接很多?
├── 是 → Elixir / Phoenix LiveView 强项
└── 否 → 任何语言

九、回到本项目:ex/elixir 为什么选 Elixir?

这个项目(n8n 风格的工作流管道)选 Elixir 的原因:

  1. 轻量级进程:每个 DAG 步骤可以是独立进程,互不阻塞
  2. 监督树:外部 API(飞书 / 企微)挂了,supervisor 重启不影响其他
  3. Oban:基于 PG 的持久化任务队列——任务持久化在 DB,重启不丢
  4. 热代码:开发时改一行代码秒级生效(无 Spring Boot 那种几十秒启动)
  5. 错误处理{:ok, x} / {:error, e} 让 worker 失败重试逻辑极清晰
  6. Ecto:PG 集成 + 动态查询 + changeset 校验,跟 Spring Data JPA 思路相似

十、测验(8 道对比)

1

Elixir 处理"预期错误"的主要方式?

2

Go 的并发模型和 Elixir 最大的差异?

3

Clojure 跟 Elixir 最像的一点?

4

Java 的 Thread 和 Elixir 进程的区别?

5

Kotlin Coroutine 跟 Elixir 进程的对比?

6

哪个语言最像 Elixir 的运行平台?

7

99.999% 可用性的应用选哪个?

8

Phoenix LiveView 主要擅长?

**下一步:**看 Lesson 0005 · 综合测验 + 代码练习