跳转至

Gleam 语言入门

为什么要学

Gleam 是运行在 Erlang VM (BEAM) 上的类型安全函数式语言,结合了 Erlang 的并发能力和现代语言的开发体验:

  • 类型安全:静态类型+类型推断,编译时捕获错误
  • BEAM 并发:继承 Erlang/Elixir 的 Actor 模型和容错能力
  • 友好语法:类似 Rust/Swift 的现代语法,非 Erlang 的怪异语法
  • 互操作:可以调用 Erlang/Elixir 生态的所有库
  • JS 编译:同一份代码可以编译为 JavaScript 运行在浏览器/Node
  • 零运行时错误:类型系统设计目标是消除运行时异常

如果你想用 Erlang VM 的强大并发能力但不想学 Erlang 的语法,Gleam 是完美的桥梁。

核心概念

白话解释

  • BEAM VM:Erlang 的虚拟机,以高并发和容错闻名(WhatsApp 就用这个)
  • 不可变数据:数据一旦创建就不能修改,要改就创建新的
  • 模式匹配:像"拆包裹"一样检查和解构数据
  • Result 类型:用 Ok/Error 表示操作结果,不用异常机制
  • 管道运算符|> 把前一步的结果传给下一步,代码像读句子

核心概念对照表

概念Gleam其他语言类比
类型推断自动推断,可省略注解TypeScript/Rust
模式匹配case expressionRust match
管道\|> 运算符Unix管道 \|
ResultOk(value) / Error(e)Rust Result
OptionSome(v) / NoneRust Option
Module文件即模块Python/Elixir
OTP进程/消息/监督树Erlang Actor
Use类似do-notation简化回调嵌套

安装配置

安装

# macOS
brew install gleam erlang rebar3

# Linux (预编译)
curl -fsSL https://gleam.run/install.sh | sh

# 确保Erlang已安装
# Ubuntu
sudo apt install erlang

# 验证
gleam --version

创建项目

gleam new my_app
cd my_app

# 项目结构:
# ├── gleam.toml
# ├── src/
# │   └── my_app.gleam
# └── test/
#     └── my_app_test.gleam

gleam.toml

name = "my_app"
version = "1.0.0"
target = "erlang"  # 或 "javascript"

[dependencies]
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
gleam_erlang = ">= 0.25.0 and < 1.0.0"

[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"

快速上手

基础语法

// src/my_app.gleam
import gleam/io
import gleam/int
import gleam/string

pub fn main() {
  // 变量绑定(不可变)
  let name = "Gleam"
  let age = 3

  // 字符串拼接
  let greeting = "Hello, " <> name <> "!"
  io.println(greeting)

  // 整数转字符串
  io.println("Age: " <> int.to_string(age))

  // 管道运算符
  "hello world"
  |> string.uppercase
  |> string.reverse
  |> io.println
}

类型和函数

import gleam/float
import gleam/int

// 自定义类型(类似enum)
pub type Shape {
  Circle(radius: Float)
  Rectangle(width: Float, height: Float)
  Triangle(base: Float, height: Float)
}

// 函数+模式匹配
pub fn area(shape: Shape) -> Float {
  case shape {
    Circle(r) -> float.pi *. r *. r
    Rectangle(w, h) -> w *. h
    Triangle(b, h) -> 0.5 *. b *. h
  }
}

// 泛型函数
pub fn first(list: List(a)) -> Result(a, Nil) {
  case list {
    [head, ..] -> Ok(head)
    [] -> Error(Nil)
  }
}

管道和链式处理

import gleam/list
import gleam/int
import gleam/string

pub fn process_numbers() -> String {
  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  |> list.filter(fn(x) { x > 3 })
  |> list.map(fn(x) { x * 2 })
  |> list.fold(0, int.add)
  |> int.to_string
  |> string.append(" is the result")
}

Result 错误处理

import gleam/int
import gleam/result

pub type AppError {
  ParseError(String)
  NotFound
  Unauthorized
}

pub fn parse_age(input: String) -> Result(Int, AppError) {
  input
  |> int.parse
  |> result.map_error(fn(_) { ParseError("无法解析年龄: " <> input) })
}

pub fn validate_age(age: Int) -> Result(Int, AppError) {
  case age {
    a if a >= 0 && a <= 150 -> Ok(a)
    _ -> Error(ParseError("年龄不合法"))
  }
}

// 使用use简化Result链
pub fn process_input(input: String) -> Result(String, AppError) {
  use age <- result.try(parse_age(input))
  use valid_age <- result.try(validate_age(age))
  Ok("年龄: " <> int.to_string(valid_age))
}

进阶用法

1. OTP 并发(Actor)

import gleam/erlang/process.{type Subject}
import gleam/otp/actor

// 定义消息类型
pub type Message {
  Increment
  Decrement
  GetCount(reply_to: Subject(Int))
}

// Actor状态
pub type State = Int

// 创建counter actor
pub fn start_counter() -> Result(Subject(Message), actor.StartError) {
  actor.start(0, fn(message: Message, state: State) -> actor.Next(Message, State) {
    case message {
      Increment -> actor.continue(state + 1)
      Decrement -> actor.continue(state - 1)
      GetCount(reply_to) -> {
        process.send(reply_to, state)
        actor.continue(state)
      }
    }
  })
}

// 使用
pub fn main() {
  let assert Ok(counter) = start_counter()

  process.send(counter, Increment)
  process.send(counter, Increment)
  process.send(counter, Increment)

  let count = process.call(counter, GetCount, 1000)
  // count = 3
}

2. Web 开发(Wisp框架)

import gleam/http/request.{type Request}
import gleam/http/response.{type Response}
import wisp.{type Request as WispRequest}
import mist

pub fn main() {
  let assert Ok(_) =
    wisp.mist_handler(handle_request, "secret")
    |> mist.new
    |> mist.port(8000)
    |> mist.start_http

  process.sleep_forever()
}

fn handle_request(req: WispRequest) -> Response(wisp.Body) {
  case wisp.path_segments(req) {
    [] -> wisp.ok() |> wisp.string_body("Hello Gleam!")
    ["api", "users"] -> handle_users(req)
    _ -> wisp.not_found()
  }
}

fn handle_users(req: WispRequest) -> Response(wisp.Body) {
  case req.method {
    http.Get -> {
      let json = "[{\"name\": \"张三\"}]"
      wisp.ok()
      |> wisp.set_header("content-type", "application/json")
      |> wisp.string_body(json)
    }
    _ -> wisp.method_not_allowed([http.Get])
  }
}

3. JSON 处理

import gleam/json
import gleam/dynamic

pub type User {
  User(name: String, age: Int, email: String)
}

// 编码
pub fn encode_user(user: User) -> String {
  json.object([
    #("name", json.string(user.name)),
    #("age", json.int(user.age)),
    #("email", json.string(user.email)),
  ])
  |> json.to_string
}

// 解码
pub fn decode_user(json_string: String) -> Result(User, json.DecodeError) {
  let decoder = dynamic.decode3(
    User,
    dynamic.field("name", dynamic.string),
    dynamic.field("age", dynamic.int),
    dynamic.field("email", dynamic.string),
  )

  json.decode(from: json_string, using: decoder)
}

4. 编译到 JavaScript

# gleam.toml
target = "javascript"
// 可以导入JS模块
@external(javascript, "./ffi.mjs", "readFile")
pub fn read_file(path: String) -> String
# 编译为JS
gleam build --target javascript

# 运行
gleam run --target javascript

5. 测试

// test/my_app_test.gleam
import gleeunit
import gleeunit/should
import my_app

pub fn main() {
  gleeunit.main()
}

pub fn area_circle_test() {
  my_app.area(my_app.Circle(1.0))
  |> should.equal(3.141592653589793)
}

pub fn parse_age_valid_test() {
  my_app.parse_age("25")
  |> should.equal(Ok(25))
}

pub fn parse_age_invalid_test() {
  my_app.parse_age("abc")
  |> should.be_error
}
gleam test

常见问题

Q1: Gleam vs Elixir?

方面GleamElixir
类型系统静态类型动态类型
语法类C/Rust类Ruby
错误处理Result类型异常+模式匹配
生态小但增长成熟丰富
互操作可调用Erlang/Elixir可调用Erlang
适合想要类型安全想要灵活性

Q2: 性能如何?

运行在 BEAM VM 上,性能与 Erlang/Elixir 相当。适合 I/O 密集型和高并发场景,不适合纯 CPU 计算密集型任务。

Q3: 生态足够吗?

Gleam 自己的包还在增长中,但可以直接使用整个 Erlang/Elixir 生态(通过 FFI),所以实际可用的库非常多。

Q4: 适合什么场景?

  • 高并发 Web 服务
  • 实时系统(聊天、游戏服务器)
  • 分布式系统
  • 需要高可用性的后端服务

参考资源