名称: Gleam 类型系统 用户可调用: false 描述: 当需要Gleam的类型系统,包括代数数据类型、自定义类型、模式匹配、泛型类型、类型推断、不透明类型、穷举检查和函数式错误处理,用于构建类型安全的Erlang VM应用程序时使用。 允许工具: []
Gleam 类型系统
介绍
Gleam 是一种静态类型的函数式语言,可编译到 Erlang 和 JavaScript,为 BEAM 生态系统带来现代类型安全。其类型系统在保持 Erlang VM 的并发性和容错性优势的同时,防止了整类运行时错误。
类型系统特点包括代数数据类型、参数多态、类型推断、穷举模式匹配和无空值。每个值都有类型,编译器在编译时强制执行类型安全,在代码运行前消除常见错误。
本技能涵盖自定义类型和ADTs、模式匹配、泛型类型、Result 和 Option 类型、类型别名、不透明类型、类型推断以及BEAM上类型安全错误处理的模式。
自定义类型和记录
自定义类型定义了具有命名字段的结构化数据,提供类型安全访问和模式匹配。
// 简单自定义类型(记录)
pub type User {
User(name: String, age: Int, email: String)
}
// 创建实例
pub fn create_user() -> User {
User(name: "Alice", age: 30, email: "alice@example.com")
}
// 访问字段
pub fn get_user_name(user: User) -> String {
user.name
}
pub fn get_user_age(user: User) -> Int {
user.age
}
// 更新记录(不可变)
pub fn birthday(user: User) -> User {
User(..user, age: user.age + 1)
}
pub fn change_email(user: User, new_email: String) -> User {
User(..user, email: new_email)
}
// 多个构造函数
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(radius) -> 3.14159 *. radius *. radius
Rectangle(width, height) -> width *. height
Triangle(base, height) -> base *. height /. 2.0
}
}
// 元组结构(未标记字段)
pub type Point {
Point(Float, Float)
}
pub fn distance(p1: Point, p2: Point) -> Float {
let Point(x1, y1) = p1
let Point(x2, y2) = p2
let dx = x2 -. x1
let dy = y2 -. y1
float.square_root(dx *. dx +. dy *. dy)
}
// 嵌套自定义类型
pub type Address {
Address(street: String, city: String, zip: String)
}
pub type Person {
Person(name: String, age: Int, address: Address)
}
pub fn get_city(person: Person) -> String {
person.address.city
}
// 泛型自定义类型
pub type Box(a) {
Box(value: a)
}
pub fn box_map(box: Box(a), f: fn(a) -> b) -> Box(b) {
Box(value: f(box.value))
}
pub fn unbox(box: Box(a)) -> a {
box.value
}
// 递归类型
pub type Tree(a) {
Leaf(value: a)
Branch(left: Tree(a), right: Tree(a))
}
pub fn tree_depth(tree: Tree(a)) -> Int {
case tree {
Leaf(_) -> 1
Branch(left, right) -> 1 + int.max(tree_depth(left), tree_depth(right))
}
}
// 幻影类型用于类型安全API
pub type Validated
pub type Unvalidated
pub type Email(state) {
Email(value: String)
}
pub fn create_email(value: String) -> Email(Unvalidated) {
Email(value: value)
}
pub fn validate_email(email: Email(Unvalidated)) ->
Result(Email(Validated), String) {
case string.contains(email.value, "@") {
True -> Ok(Email(value: email.value))
False -> Error("无效邮箱格式")
}
}
pub fn send_email(email: Email(Validated)) -> Nil {
// 只有验证过的邮箱可以发送
io.println("发送邮件到: " <> email.value)
}
自定义类型提供具有穷举模式匹配保证的命名、类型安全数据结构。
代数数据类型
ADTs 用多个变体建模数据,支持穷举模式匹配并使无效状态无法表示。
// 和类型(枚举)
pub type Status {
Pending
Approved
Rejected
}
pub fn status_to_string(status: Status) -> String {
case status {
Pending -> "Pending"
Approved -> "Approved"
Rejected -> "Rejected"
}
}
// Result 类型(内置ADT)
pub type Result(ok, error) {
Ok(ok)
Error(error)
}
pub fn parse_int(str: String) -> Result(Int, String) {
case int.parse(str) {
Ok(n) -> Ok(n)
Error(_) -> Error("非有效整数")
}
}
pub fn handle_result(result: Result(Int, String)) -> String {
case result {
Ok(n) -> "得到数字: " <> int.to_string(n)
Error(msg) -> "错误: " <> msg
}
}
// Option 类型模式
pub type Option(a) {
Some(a)
None
}
pub fn find_user(id: Int) -> Option(User) {
case id {
1 -> Some(User(name: "Alice", age: 30, email: "alice@example.com"))
_ -> None
}
}
pub fn option_map(opt: Option(a), f: fn(a) -> b) -> Option(b) {
case opt {
Some(value) -> Some(f(value))
None -> None
}
}
pub fn option_unwrap_or(opt: Option(a), default: a) -> a {
case opt {
Some(value) -> value
None -> default
}
}
// 复杂ADTs
pub type HttpResponse {
Ok200(body: String)
Created201(body: String, location: String)
BadRequest400(message: String)
NotFound404
ServerError500(message: String)
}
pub fn handle_response(response: HttpResponse) -> String {
case response {
Ok200(body) -> "成功: " <> body
Created201(body, location) -> "创建于 " <> location <> ": " <> body
BadRequest400(message) -> "错误请求: " <> message
NotFound404 -> "资源未找到"
ServerError500(message) -> "服务器错误: " <> message
}
}
// 链表ADT
pub type List(a) {
Nil
Cons(head: a, tail: List(a))
}
pub fn list_length(list: List(a)) -> Int {
case list {
Nil -> 0
Cons(_, tail) -> 1 + list_length(tail)
}
}
pub fn list_map(list: List(a), f: fn(a) -> b) -> List(b) {
case list {
Nil -> Nil
Cons(head, tail) -> Cons(f(head), list_map(tail, f))
}
}
// Either 类型
pub type Either(left, right) {
Left(left)
Right(right)
}
pub fn partition_either(list: List(Either(a, b))) -> #(List(a), List(b)) {
case list {
Nil -> #(Nil, Nil)
Cons(Left(a), tail) -> {
let #(lefts, rights) = partition_either(tail)
#(Cons(a, lefts), rights)
}
Cons(Right(b), tail) -> {
let #(lefts, rights) = partition_either(tail)
#(lefts, Cons(b, rights))
}
}
}
// 带ADTs的状态机
pub type ConnectionState {
Disconnected
Connecting(attempt: Int)
Connected(session_id: String)
Disconnecting
}
pub fn handle_connect_event(state: ConnectionState) -> ConnectionState {
case state {
Disconnected -> Connecting(attempt: 1)
Connecting(attempt) if attempt < 3 -> Connecting(attempt: attempt + 1)
Connecting(_) -> Disconnected
Connected(_) -> state
Disconnecting -> state
}
}
// 表达式树ADT
pub type Expr {
Number(Float)
Add(left: Expr, right: Expr)
Subtract(left: Expr, right: Expr)
Multiply(left: Expr, right: Expr)
Divide(left: Expr, right: Expr)
}
pub fn evaluate(expr: Expr) -> Result(Float, String) {
case expr {
Number(n) -> Ok(n)
Add(left, right) -> {
use l <- result.try(evaluate(left))
use r <- result.try(evaluate(right))
Ok(l +. r)
}
Subtract(left, right) -> {
use l <- result.try(evaluate(left))
use r <- result.try(evaluate(right))
Ok(l -. r)
}
Multiply(left, right) -> {
use l <- result.try(evaluate(left))
use r <- result.try(evaluate(right))
Ok(l *. r)
}
Divide(left, right) -> {
use l <- result.try(evaluate(left))
use r <- result.try(evaluate(right))
case r {
0.0 -> Error("除以零")
_ -> Ok(l /. r)
}
}
}
}
ADTs 允许用编译器验证穷举性,类型安全地建模复杂领域逻辑。
模式匹配
模式匹配提供穷举、类型安全的条件逻辑,具有解构能力。
// 基本模式匹配
pub fn describe_number(n: Int) -> String {
case n {
0 -> "零"
1 -> "一"
2 -> "二"
_ -> "多"
}
}
// 带守卫的模式匹配
pub fn classify_age(age: Int) -> String {
case age {
n if n < 0 -> "无效"
n if n < 13 -> "儿童"
n if n < 20 -> "青少年"
n if n < 65 -> "成人"
_ -> "老年人"
}
}
// 解构元组
pub fn swap(pair: #(a, b)) -> #(b, a) {
let #(first, second) = pair
#(second, first)
}
pub fn tuple_pattern(tuple: #(Int, String, Bool)) -> String {
case tuple {
#(0, _, _) -> "第一个是零"
#(_, "hello", _) -> "第二个是hello"
#(_, _, True) -> "第三个是真"
_ -> "其他"
}
}
// 解构自定义类型
pub fn greet_user(user: User) -> String {
let User(name: name, age: age, email: _) = user
"你好 " <> name <> ", 你 " <> int.to_string(age) <> " 岁"
}
pub fn is_circle(shape: Shape) -> Bool {
case shape {
Circle(_) -> True
_ -> False
}
}
// 嵌套模式匹配
pub type Nested {
Outer(inner: Inner)
}
pub type Inner {
Value(Int)
Empty
}
pub fn extract_value(nested: Nested) -> Option(Int) {
case nested {
Outer(Value(n)) -> Some(n)
Outer(Empty) -> None
}
}
// 列表模式匹配
pub fn list_sum(list: List(Int)) -> Int {
case list {
[] -> 0
[head] -> head
[first, second] -> first + second
[head, ..tail] -> head + list_sum(tail)
}
}
pub fn list_head(list: List(a)) -> Option(a) {
case list {
[] -> None
[head, ..] -> Some(head)
}
}
// 多case表达式
pub fn compare_results(r1: Result(Int, String),
r2: Result(Int, String)) -> String {
case r1, r2 {
Ok(n1), Ok(n2) -> "都成功: " <> int.to_string(n1 + n2)
Ok(n), Error(_) -> "第一个成功: " <> int.to_string(n)
Error(_), Ok(n) -> "第二个成功: " <> int.to_string(n)
Error(e1), Error(e2) -> "都失败: " <> e1 <> ", " <> e2
}
}
// 带替代模式的模式匹配
pub fn is_weekend(day: String) -> Bool {
case day {
"Saturday" | "Sunday" -> True
_ -> False
}
}
// 字符串模式匹配
pub fn parse_command(input: String) -> String {
case string.lowercase(input) {
"quit" | "exit" | "q" -> "退出中..."
"help" | "h" | "?" -> "帮助消息"
_ -> "未知命令"
}
}
// 使用表达式处理结果
pub fn divide_and_double(a: Int, b: Int) -> Result(Int, String) {
use quotient <- result.try(case b {
0 -> Error("除以零")
_ -> Ok(a / b)
})
Ok(quotient * 2)
}
// 枚举的穷举匹配
pub fn status_code(status: Status) -> Int {
case status {
Pending -> 0
Approved -> 1
Rejected -> 2
}
}
模式匹配支持简洁、穷举的条件逻辑,具有编译时验证。
泛型类型和多态
泛型类型允许编写可重用代码,适用于多种类型,同时保持类型安全。
// 泛型函数
pub fn identity(value: a) -> a {
value
}
pub fn const(a: a, b: b) -> a {
a
}
// 泛型数据结构
pub type Pair(a, b) {
Pair(first: a, second: b)
}
pub fn pair_map_first(pair: Pair(a, b), f: fn(a) -> c) -> Pair(c, b) {
Pair(first: f(pair.first), second: pair.second)
}
pub fn pair_map_second(pair: Pair(a, b), f: fn(b) -> c) -> Pair(a, c) {
Pair(first: pair.first, second: f(pair.second))
}
pub fn pair_swap(pair: Pair(a, b)) -> Pair(b, a) {
Pair(first: pair.second, second: pair.first)
}
// 泛型容器
pub type Container(a) {
Empty
Full(value: a)
}
pub fn container_map(cont: Container(a), f: fn(a) -> b) -> Container(b) {
case cont {
Empty -> Empty
Full(value) -> Full(f(value))
}
}
pub fn container_unwrap_or(cont: Container(a), default: a) -> a {
case cont {
Empty -> default
Full(value) -> value
}
}
// 高阶函数
pub fn map(list: List(a), f: fn(a) -> b) -> List(b) {
case list {
[] -> []
[head, ..tail] -> [f(head), ..map(tail, f)]
}
}
pub fn filter(list: List(a), predicate: fn(a) -> Bool) -> List(a) {
case list {
[] -> []
[head, ..tail] -> case predicate(head) {
True -> [head, ..filter(tail, predicate)]
False -> filter(tail, predicate)
}
}
}
pub fn fold(list: List(a), initial: b, f: fn(b, a) -> b) -> b {
case list {
[] -> initial
[head, ..tail] -> fold(tail, f(initial, head), f)
}
}
// 泛型Result操作
pub fn result_map(result: Result(a, e), f: fn(a) -> b) -> Result(b, e) {
case result {
Ok(value) -> Ok(f(value))
Error(err) -> Error(err)
}
}
pub fn result_map_error(result: Result(a, e), f: fn(e) -> f) -> Result(a, f) {
case result {
Ok(value) -> Ok(value)
Error(err) -> Error(f(err))
}
}
pub fn result_and_then(
result: Result(a, e),
f: fn(a) -> Result(b, e),
) -> Result(b, e) {
case result {
Ok(value) -> f(value)
Error(err) -> Error(err)
}
}
pub fn result_unwrap_or(result: Result(a, e), default: a) -> a {
case result {
Ok(value) -> value
Error(_) -> default
}
}
// 组合Results
pub fn result_all(results: List(Result(a, e))) -> Result(List(a), e) {
case results {
[] -> Ok([])
[Ok(value), ..rest] -> {
use tail <- result_and_then(result_all(rest))
Ok([value, ..tail])
}
[Error(err), ..] -> Error(err)
}
}
// 泛型树操作
pub fn tree_map(tree: Tree(a), f: fn(a) -> b) -> Tree(b) {
case tree {
Leaf(value) -> Leaf(f(value))
Branch(left, right) -> Branch(tree_map(left, f), tree_map(right, f))
}
}
pub fn tree_fold(tree: Tree(a), initial: b, f: fn(b, a) -> b) -> b {
case tree {
Leaf(value) -> f(initial, value)
Branch(left, right) -> {
let left_result = tree_fold(left, initial, f)
tree_fold(right, left_result, f)
}
}
}
// 函子模式
pub fn functor_compose(
fa: Container(a),
f: fn(a) -> b,
g: fn(b) -> c,
) -> Container(c) {
container_map(container_map(fa, f), g)
}
泛型类型支持跨不同具体类型的可重用、类型安全抽象。
类型别名和不透明类型
类型别名创建可读名称给复杂类型,而不透明类型隐藏实现细节。
// 类型别名
pub type UserId = Int
pub type Email = String
pub type Age = Int
pub type UserData = #(UserId, String, Email, Age)
pub fn create_user_data(id: UserId, name: String, email: Email, age: Age) ->
UserData {
#(id, name, email, age)
}
// 函数类型别名
pub type Validator(a) = fn(a) -> Result(a, String)
pub type Transformer(a, b) = fn(a) -> b
pub fn validate_age(age: Age) -> Result(Age, String) {
case age >= 0 && age <= 150 {
True -> Ok(age)
False -> Error("无效年龄")
}
}
// 集合类型别名
pub type StringList = List(String)
pub type IntResult = Result(Int, String)
pub type UserMap = Dict(UserId, User)
// 不透明类型(隐藏内部表示)
pub opaque type Password {
Password(hash: String)
}
pub fn create_password(plain: String) -> Password {
// 哈希密码(简化)
Password(hash: hash_string(plain))
}
pub fn verify_password(password: Password, plain: String) -> Bool {
let Password(hash: stored_hash) = password
stored_hash == hash_string(plain)
}
fn hash_string(s: String) -> String {
// 实现隐藏
s <> "_hashed"
}
// 不透明类型用于已验证数据
pub opaque type ValidatedEmail {
ValidatedEmail(value: String)
}
pub fn validate_and_create_email(value: String) ->
Result(ValidatedEmail, String) {
case string.contains(value, "@") {
True -> Ok(ValidatedEmail(value: value))
False -> Error("无效邮箱格式")
}
}
pub fn email_to_string(email: ValidatedEmail) -> String {
let ValidatedEmail(value: value) = email
value
}
// 不透明类型用于单位
pub opaque type Meters {
Meters(Float)
}
pub opaque type Feet {
Feet(Float)
}
pub fn meters(value: Float) -> Meters {
Meters(value)
}
pub fn feet(value: Float) -> Feet {
Feet(value)
}
pub fn meters_to_feet(m: Meters) -> Feet {
let Meters(value) = m
Feet(value *. 3.28084)
}
pub fn feet_to_meters(f: Feet) -> Meters {
let Feet(value) = f
Meters(value /. 3.28084)
}
// 不透明类型用于IDs
pub opaque type OrderId {
OrderId(Int)
}
pub fn new_order_id(id: Int) -> OrderId {
OrderId(id)
}
pub fn order_id_to_int(id: OrderId) -> Int {
let OrderId(value) = id
value
}
// 带不透明类型的构建器模式
pub opaque type Query {
Query(table: String, conditions: List(String), limit: Option(Int))
}
pub fn new_query(table: String) -> Query {
Query(table: table, conditions: [], limit: None)
}
pub fn where(query: Query, condition: String) -> Query {
let Query(table: table, conditions: conditions, limit: limit) = query
Query(table: table, conditions: [condition, ..conditions], limit: limit)
}
pub fn limit(query: Query, n: Int) -> Query {
let Query(table: table, conditions: conditions, limit: _) = query
Query(table: table, conditions: conditions, limit: Some(n))
}
pub fn to_sql(query: Query) -> String {
let Query(table: table, conditions: conditions, limit: limit) = query
let where_clause = case conditions {
[] -> ""
_ -> " WHERE " <> string.join(conditions, " AND ")
}
let limit_clause = case limit {
None -> ""
Some(n) -> " LIMIT " <> int.to_string(n)
}
"SELECT * FROM " <> table <> where_clause <> limit_clause
}
类型别名提高可读性,而不透明类型强制不变性并隐藏实现细节。
最佳实践
-
使用自定义类型进行领域建模 以在编译时使无效状态无法表示
-
利用模式匹配穷举性 确保处理所有情况,无需运行时检查
-
首选Result而非异常 用于预期错误,使错误处理显式化
-
使用不透明类型进行验证 防止在模块外创建无效值
-
应用泛型类型 当算法适用于多种类型时,最大化代码重用
-
使用类型别名 提高复杂类型的可读性和可维护性
-
模式匹配特定变体 而非使用通配符模式,提高安全性
-
使用幻影类型 用于状态机或工作流中的编译时状态跟踪
-
避免嵌套Results 通过使用 result.try 或 use 表达式,简化错误处理
-
文档化不透明类型不变量 澄清抽象强制约束
常见陷阱
-
过度使用泛型类型 当特定类型足够时,增加复杂性而无益处
-
不使用不透明类型 暴露内部表示并破坏封装
-
忽略编译器警告 关于非穷举模式,导致运行时崩溃
-
创建冗余类型别名 对于简单类型,降低清晰度
-
在边界处不验证 当使用不透明类型时,允许无效数据创建
-
在模式中过度使用下划线 错过有价值的解构机会
-
嵌套太多Results 创建回调式复杂性;使用use表达式
-
在模式中不使用守卫 当需要条件时,导致冗长case表达式
-
创建过于复杂的ADTs 变体太多,降低可维护性
-
忘记公共函数的类型注解 降低文档清晰度
何时使用此技能
应用自定义类型时建模具有特定字段和行为的领域实体。
使用ADTs当数据可以存在于多个状态或具有不同属性的变体中。
利用模式匹配用于所有需要解构或穷举性的条件逻辑。
应用泛型类型当实现可重用算法或数据结构时。
使用不透明类型当强制不变量或隐藏模块用户的实现细节时。
使用Result类型用于所有可能失败的操作,使错误处理显式化。