Gleam คือภาษาโปรแกรมแบบ Functional Programming ที่ถูกออกแบบมาเพื่อทำงานบน BEAM Virtual Machine (Erlang VM) โดยให้ความสำคัญกับ Type Safety สูงสุด กล่าวคือ ข้อผิดพลาดส่วนใหญ่จะถูกตรวจพบในขั้นตอนการ Compile ก่อนที่โปรแกรมจะถูกรันจริง
ภาษา Gleam ถูกสร้างโดย Louis Pilfold และเปิดตัวเวอร์ชันเสถียร (v1.0) ในปี ค.ศ. 2024 โดยได้รับแรงบันดาลใจจากภาษา Rust ในแง่ของระบบ Type และความปลอดภัย และจากภาษา Elm ในแง่ของความเรียบง่ายและ Developer Experience ที่ดี
null ไม่มี Runtime Exception แบบซ่อนเร้น
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#504945', 'lineColor': '#a89984', 'secondaryColor': '#3c3836', 'tertiaryColor': '#32302f', 'background': '#282828', 'mainBkg': '#3c3836', 'nodeBorder': '#928374', 'clusterBkg': '#32302f', 'titleColor': '#ebdbb2', 'edgeLabelBackground': '#504945', 'fontFamily': 'monospace'}}}%%
flowchart LR
subgraph e["🌱 ยุคก่อนกำเนิด (Pre-2016)"]
E1["Erlang\n(1986)\nต้นกำเนิด BEAM"]
E2["Elixir\n(2012)\nภาษาแรกบน BEAM\nที่ทันสมัย"]
end
subgraph a["⚡ ยุคกำเนิด (2016-2021)"]
G1["Gleam v0.1\n(2018)\nPrototype แรก"]
G2["Gleam v0.14\n(2021)\nType System\nเสถียร"]
end
subgraph g["🚀 ยุคเติบโต (2022-2024)"]
G3["Gleam v0.25\n(2022)\nJS Target\nรองรับ"]
G4["Gleam v1.0\n(2024)\nเสถียรภาพเต็มรูปแบบ"]
end
E1 --> E2
E2 --> G1
G1 --> G2
G2 --> G3
G3 --> G4
BEAM (Bogdan/Björn's Erlang Abstract Machine) คือ Virtual Machine ที่ถูกออกแบบโดย Ericsson สำหรับระบบโทรคมนาคม ซึ่งต้องการ Uptime 99.9999999% (Nine nines) หรือ Downtime ไม่เกิน 31 มิลลิวินาทีต่อปี
| คุณสมบัติ | คำอธิบาย | ผลลัพธ์ |
|---|---|---|
| Lightweight Processes | แต่ละ Process ใช้ RAM เพียง ~2KB | รองรับล้าน Processes พร้อมกันได้ |
| Preemptive Scheduling | Scheduler จัดการเวลาให้ทุก Process ยุติธรรม | ไม่มี Process ใดครองเวลานานเกินไป |
| Garbage Collection per Process | GC ทำงานแยกกันในแต่ละ Process | ไม่มี Stop-the-world GC |
| Hot Code Reloading | อัปเดต Code ได้โดยไม่ต้องหยุดระบบ | Zero-downtime Deployment |
| Built-in Distribution | ส่ง Message ข้าม Node ได้โดยตรง | Distributed System พร้อมใช้งาน |
| Let it Crash | Process ล้มเหลวโดดเดี่ยว ไม่กระทบส่วนอื่น | Fault-tolerant โดยธรรมชาติ |
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#504945', 'lineColor': '#b8bb26', 'secondaryColor': '#3c3836', 'background': '#282828', 'mainBkg': '#3c3836', 'nodeBorder': '#b8bb26', 'clusterBkg': '#1d2021', 'titleColor': '#fabd2f', 'fontFamily': 'monospace'}}}%%
graph TD
subgraph OS["ระบบปฏิบัติการ (Operating System)"]
subgraph BEAM_VM["BEAM Virtual Machine"]
subgraph Schedulers["Schedulers (1 ต่อ CPU Core)"]
S1["Scheduler 1\nCore 0"]
S2["Scheduler 2\nCore 1"]
S3["Scheduler N\nCore N"]
end
subgraph Processes["BEAM Processes (Lightweight)"]
P1["Process A\n~2KB RAM"]
P2["Process B\n~2KB RAM"]
P3["Process C\n~2KB RAM"]
P4["Process D\n~2KB RAM"]
end
MB["Message Box\n(Mailbox ประจำแต่ละ Process)"]
end
end
S1 --> P1
S1 --> P2
S2 --> P3
S3 --> P4
P1 -- "ส่ง Message" --> MB
MB -- "รับ Message" --> P2
| ด้าน | Erlang | Elixir | Gleam |
|---|---|---|---|
| Syntax | เก่า, อ่านยาก | Ruby-like, เข้าถึงง่าย | Rust/ML-like, ทันสมัย |
| Type System | Dynamic (ไม่มี Static Type) | Dynamic (ไม่มี Static Type) | Static, Strong |
| Error Detection | Runtime | Runtime | Compile-time |
| Learning Curve | สูงมาก | ปานกลาง | ต่ำ-ปานกลาง |
| JavaScript Target | ไม่มี | ไม่มี | รองรับ |
| Null Safety | ไม่มี (atom undefined) |
ไม่มี (nil) |
ไม่มี null |
ก่อนติดตั้ง Gleam ต้องมีสิ่งต่อไปนี้:
macOS (Homebrew):
# ติดตั้ง Erlang ก่อน
brew install erlang
# ติดตั้ง Gleam
brew install gleam
# ตรวจสอบเวอร์ชัน
gleam --version
Linux (asdf — แนะนำที่สุด):
# ติดตั้ง asdf version manager
git clone https://github.com/asdf-vm/asdf.git ~/.asdf
# เพิ่ม plugin สำหรับ Erlang และ Gleam
asdf plugin add erlang
asdf plugin add gleam
# ติดตั้งเวอร์ชันล่าสุด
asdf install erlang latest
asdf install gleam latest
asdf global erlang latest
asdf global gleam latest
# ตรวจสอบ
gleam --version # ควรแสดง gleam 1.x.x
Windows:
# ใช้ Scoop package manager
scoop install erlang
scoop install gleam
# สร้างโปรเจกต์ใหม่
gleam new hello_gleam
# โครงสร้างที่ได้
hello_gleam/
├── gleam.toml # Project configuration
├── README.md
├── src/
│ └── hello_gleam.gleam # Main source file
└── test/
└── hello_gleam_test.gleam # Test file
# เข้าโปรเจกต์และรัน
cd hello_gleam
gleam run
ตัวอย่าง gleam.toml:
name = "hello_gleam"
version = "1.0.0"
description = "โปรเจกต์ Gleam แรกของฉัน"
[dependencies]
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"
Ctrl+Shift+X)settings.json:{
"editor.formatOnSave": true,
"[gleam]": {
"editor.defaultFormatter": "Gleam.gleam",
"editor.tabSize": 2
}
}
Zed รองรับ Gleam ผ่าน Language Server โดยอัตโนมัติ:
Cmd+,)settings.json:{
"languages": {
"Gleam": {
"format_on_save": "on",
"tab_size": 2
}
}
}
| คำสั่ง | หน้าที่ | ใช้เมื่อไหร่ |
|---|---|---|
gleam new <name> |
สร้างโปรเจกต์ใหม่ | เริ่มต้นโปรเจกต์ |
gleam run |
Compile และรัน | ทดสอบโปรแกรม |
gleam build |
Compile โดยไม่รัน | ตรวจสอบ Error |
gleam check |
ตรวจสอบ Type เท่านั้น | เร็วกว่า build |
gleam test |
รัน Test ทั้งหมด | ตรวจสอบความถูกต้อง |
gleam format |
จัดรูปแบบ Code | ก่อน Commit |
gleam add <pkg> |
เพิ่ม Dependency | เพิ่ม Library |
gleam docs build |
สร้าง Documentation | เตรียม Publish |
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#3c3836', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#928374', 'lineColor': '#8ec07c', 'background': '#282828', 'mainBkg': '#3c3836', 'nodeBorder': '#8ec07c', 'titleColor': '#fabd2f', 'fontFamily': 'monospace'}}
}%%
flowchart TD
A["✏️ เขียน Code\n(src/*.gleam)"] --> B["⚡ gleam check\nตรวจ Type"]
B --> C{Type ถูกต้อง?}
C -- "❌ มี Error" --> A
C -- "✅ ผ่าน" --> D["🧪 gleam test\nรัน Test"]
D --> E{Test ผ่าน?}
E -- "❌ ล้มเหลว" --> A
E -- "✅ ผ่านทั้งหมด" --> F["🎨 gleam format\nจัด Code Style"]
F --> G["📦 git commit\nบันทึกการเปลี่ยนแปลง"]
G --> H["🚀 gleam build\nBuild สำหรับ Production"]
ใน Gleam ตัวแปรทุกตัว Immutable (เปลี่ยนแปลงค่าไม่ได้) โดยค่าเริ่มต้น ซึ่งเป็นรากฐานสำคัญของ Functional Programming ที่ทำให้โปรแกรมคาดเดาได้ง่ายและทดสอบได้ง่าย
// ตัวอย่างการประกาศตัวแปร
import gleam/io
pub fn main() {
// let — ประกาศตัวแปรปกติ (Immutable)
let name = "สมชาย"
let age = 25
let score = 98.5
io.println("ชื่อ: " <> name)
// ❌ ทำแบบนี้ไม่ได้ — Gleam ไม่อนุญาต reassignment
// name = "สมหญิง" // Compile Error!
// ✅ แต่สามารถ Shadow ตัวแปรได้ (ประกาศใหม่ด้วยชื่อเดิม)
let name = "สมหญิง" // นี่คือตัวแปรใหม่ ไม่ใช่การแก้ค่าเดิม
io.println("ชื่อใหม่: " <> name)
}
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#3c3836', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#504945', 'lineColor': '#fabd2f', 'background': '#282828', 'mainBkg': '#3c3836', 'clusterBkg': '#1d2021', 'titleColor': '#fabd2f', 'fontFamily': 'monospace'}}
}%%
graph LR
subgraph a["✅ ข้อดีของ Immutability"]
A["🔍 Debug ง่าย\nค่าไม่เปลี่ยนลึกลับ"]
B["🧵 Thread-safe\nหลาย Process ใช้ร่วมกัน\nไม่มี Race Condition"]
C["🧪 Test ง่าย\nฟังก์ชันให้ผลเหมือนกันเสมอ"]
D["🧠 คาดเดาง่าย\nอ่าน Code แล้วรู้ค่าแน่นอน"]
end
E["Immutable\nVariables"] --> A
E --> B
E --> C
E --> D
| Type | ตัวอย่าง | คำอธิบาย |
|---|---|---|
Int |
42, -7, 0 |
จำนวนเต็ม (ไม่จำกัดขนาด) |
Float |
3.14, -0.5 |
ทศนิยม (IEEE 754 64-bit) |
String |
"สวัสดี", "Hello" |
ข้อความ UTF-8 |
Bool |
True, False |
ค่าความจริง |
Nil |
Nil |
ค่าว่าง (ไม่ใช่ null!) |
import gleam/io
import gleam/string
import gleam/int
pub fn main() {
// การต่อ String ด้วย <>
let first_name = "สม"
let last_name = "ชาย"
let full_name = first_name <> last_name
io.println(full_name) // → สมชาย
// ฟังก์ชันใน gleam/string
let text = "Hello, Gleam!"
io.println(string.length(text) |> int.to_string) // → 13
io.println(string.uppercase(text)) // → HELLO, GLEAM!
io.println(string.lowercase(text)) // → hello, gleam!
io.println(string.reverse(text)) // → !maelG ,olleH
io.println(string.contains(text, "Gleam") |> string.inspect) // → True
// String Interpolation ผ่าน string.concat หรือ int.to_string
let age = 20
let intro_text = "ฉันอายุ " <> int.to_string(age) <> " ปี"
io.println(intro_text) // → ฉันอายุ 20 ปี
// ตัด String
let sliced = string.slice(text, 7, 5)
io.println(sliced) // → Gleam
// แยก String
let parts = string.split("a,b,c,d", ",")
io.println(string.inspect(parts)) // → ["a", "b", "c", "d"]
}
import gleam/io
import gleam/erlang // สำหรับรับ Input (Erlang Target เท่านั้น)
pub fn main() {
// แสดงผลพื้นฐาน
io.println("สวัสดี Gleam! 🎉") // พิมพ์พร้อม newline
io.print("กำลังโหลด...") // พิมพ์ไม่มี newline
io.println("") // ขึ้นบรรทัดใหม่
// Debug print (แสดง Type ด้วย)
io.debug(42) // → 42
io.debug([1, 2, 3]) // → [1, 2, 3]
io.debug(#("x", True)) // → #("x", True)
// รับ Input จาก Keyboard (Erlang Target)
// io.println("กรอกชื่อของคุณ: ")
// let name = erlang.get_line("")
// io.println("สวัสดี " <> name)
}
Labelled Arguments คือการส่ง Argument โดยระบุชื่อ Parameter ทำให้ Code อ่านเข้าใจง่ายขึ้นมาก โดยเฉพาะเมื่อฟังก์ชันมี Parameter หลายตัวที่มีชนิดเดียวกัน
import gleam/io
import gleam/string
// กำหนดฟังก์ชันที่มี Labelled Arguments
fn create_profile(
name student_name: String, // label: name, param: student_name
age student_age: Int,
major field_of_study: String,
) -> String {
student_name
<> " อายุ "
<> string.inspect(student_age)
<> " ปี สาขา "
<> field_of_study
}
pub fn main() {
// เรียกโดยระบุ Label — เรียงลำดับยังไงก็ได้!
let profile = create_profile(
name: "นายสมศักดิ์",
major: "วิทยาการคอมพิวเตอร์", // เรียงลำดับต่างจากนิยามได้
age: 20,
)
io.println(profile)
// → นายสมศักดิ์ อายุ 20 ปี สาขา วิทยาการคอมพิวเตอร์
// เปรียบเทียบ: ถ้าไม่ใช้ Label — ต้องเรียงตามลำดับ
let profile2 = create_profile("นางสาวสมใจ", 21, "เทคโนโลยีสารสนเทศ")
io.println(profile2)
}
|>)Pipe Operator (|>) คือหนึ่งในคุณสมบัติที่ทรงพลังที่สุดของ Gleam ช่วยให้เขียน Code ที่อ่านจากซ้ายไปขวาได้ตามธรรมชาติ แทนการซ้อน Function Call
import gleam/io
import gleam/string
import gleam/list
pub fn main() {
// ❌ แบบเดิม — ซ้อนฟังก์ชัน อ่านยาก
let result1 = string.uppercase(string.trim(" hello gleam "))
io.println(result1) // → HELLO GLEAM
// ✅ แบบ Pipe — อ่านง่าย ไหลลื่น
let result2 =
" hello gleam "
|> string.trim
|> string.uppercase
io.println(result2) // → HELLO GLEAM
// ตัวอย่างซับซ้อนกว่า: ประมวลผลรายชื่อนักศึกษา
let scores = [45, 78, 92, 31, 67, 88, 55]
let summary =
scores
|> list.filter(fn(x) { x >= 50 }) // กรองเฉพาะที่ผ่าน
|> list.map(fn(x) { x + 5 }) // บวก Bonus 5 คะแนน
|> list.fold(0, fn(acc, x) { acc + x }) // รวมคะแนน
io.println("คะแนนรวมนักศึกษาที่ผ่าน (+ Bonus): " <> string.inspect(summary))
// → คะแนนรวมนักศึกษาที่ผ่าน (+ Bonus): 400
}
โดยที่ x คือค่าเริ่มต้น, f, g, h คือฟังก์ชันที่รับค่าจากซ้ายเป็น Argument แรก
Custom Types คือกระดูกสันหลังของ Gleam — ใช้ออกแบบโครงสร้างข้อมูลที่สื่อความหมายชัดเจนและปลอดภัย แบ่งเป็นสองรูปแบบหลัก:
import gleam/io
import gleam/string
// กำหนด Record Type สำหรับนักศึกษา
pub type Student {
Student(
id: String,
name: String,
last_name: String,
gpa: Float,
)
}
// Record Type สำหรับวิชา
pub type Course {
Course(
course_id: String,
course_name: String,
credits: Int,
)
}
pub fn main() {
// สร้าง Record
let student1 = Student(
id: "64001",
name: "สมชาย",
last_name: "ใจดี",
gpa: 3.75,
)
// เข้าถึง Field ด้วย .
io.println("ชื่อ: " <> student1.name <> " " <> student1.last_name)
io.println("GPA: " <> string.inspect(student1.gpa))
// อัปเดต Record (ได้ Record ใหม่ ไม่แก้ของเดิม)
let student1_updated = Student(..student1, gpa: 3.80)
io.println("GPA ใหม่: " <> string.inspect(student1_updated.gpa))
}
import gleam/io
// Variant Type สำหรับสถานะการลงทะเบียน
pub type RegistrationStatus {
Pending
Approved(date: String)
Rejected(reason: String)
Cancelled
}
// Variant Type สำหรับผลการเรียน
pub type AcademicResult {
Passed(grade: String)
Failed
Withdrawn
}
pub fn main() {
let status1 = Approved(date: "2024-01-15")
let status2 = Rejected(reason: "เอกสารไม่ครบ")
io.debug(status1)
io.debug(status2)
}
Pattern Matching คือกลไกที่ทรงพลังที่สุดใน Gleam ใช้แยกแยะข้อมูลที่มีโครงสร้างซับซ้อน พร้อม Exhaustive Check ที่ Compiler บังคับให้จัดการทุกกรณี
import gleam/io
import gleam/string
import gleam/list
pub type Shape {
Circle(radius: Float)
Rectangle(width: Float, height: Float)
Triangle(base: Float, height: Float)
}
// คำนวณพื้นที่ — Compiler บังคับจัดการทุก Variant
fn calculate_area(shape: Shape) -> Float {
case shape {
Circle(radius: r) -> 3.14159 *. r *. r
Rectangle(width: w, height: h) -> w *. h
Triangle(base: b, height: h) -> b *. h /. 2.0
}
}
pub fn main() {
let shapes = [
Circle(radius: 5.0),
Rectangle(width: 4.0, height: 6.0),
Triangle(base: 3.0, height: 8.0),
]
shapes
|> list.each(fn(shape) {
let area = calculate_area(shape)
io.println("พื้นที่: " <> string.inspect(area))
})
// → พื้นที่: 78.53975
// → พื้นที่: 24.0
// → พื้นที่: 12.0
}
import gleam/io
import gleam/string
pub fn main() {
// Match บน Tuple
let coordinate = #(10, 20)
case coordinate {
#(0, 0) -> io.println("จุดกำเนิด")
#(x, 0) -> io.println("แกน X ที่ " <> string.inspect(x))
#(0, y) -> io.println("แกน Y ที่ " <> string.inspect(y))
#(x, y) -> io.println("จุด (" <> string.inspect(x) <> ", " <> string.inspect(y) <> ")")
}
// → จุด (10, 20)
// Match บน List
let items = [1, 2, 3, 4, 5]
case items {
[] -> io.println("รายการว่าง")
[x] -> io.println("มีหนึ่งสมาชิก: " <> string.inspect(x))
[head, ..tail] -> {
io.println("ตัวแรก: " <> string.inspect(head))
io.println("ที่เหลือ: " <> string.inspect(tail))
}
}
// → ตัวแรก: 1
// → ที่เหลือ: [2, 3, 4, 5]
// Guard Clause
let score = 75
case score {
x if x >= 80 -> io.println("เกรด A")
x if x >= 70 -> io.println("เกรด B")
x if x >= 60 -> io.println("เกรด C")
_ -> io.println("ไม่ผ่าน")
}
// → เกรด B
}
import gleam/io
import gleam/list
import gleam/string
import gleam/int
// ฟังก์ชันปกติ
pub fn calculate_interest(principal: Float, rate: Float, years: Int) -> Float {
principal *. rate *. int.to_float(years)
}
// Higher-Order Function — รับฟังก์ชันเป็น Argument
pub fn process_scores(
scores: List(Int),
transform: fn(Int) -> Int,
) -> List(Int) {
list.map(scores, transform)
}
pub fn main() {
// เรียกฟังก์ชันปกติ
let interest = calculate_interest(100_000.0, 0.05, 3)
io.println("ดอกเบี้ย: " <> string.inspect(interest))
// → ดอกเบี้ย: 15000.0
// Anonymous Function (Lambda)
let add_ten = fn(x: Int) -> Int { x + 10 }
let subtract_five = fn(x) { x - 5 } // Gleam อนุมาน Type ให้
// ส่ง Anonymous Function เป็น Argument
let scores = [60, 70, 80]
let new_scores = process_scores(scores, add_ten)
io.debug(new_scores) // → [70, 80, 90]
// Closure — ใช้ตัวแปรจาก Context ภายนอก
let bonus = 15
let add_bonus = fn(x) { x + bonus }
let scores_with_bonus = process_scores(scores, add_bonus)
io.debug(scores_with_bonus) // → [75, 85, 95]
// Function Capture (Partial Application)
let double = int.multiply(_, 2) // _ คือตำแหน่งที่จะรับ Argument
io.debug(list.map([1, 2, 3], double)) // → [2, 4, 6]
}
use Expression แก้ปัญหา Callback Hell (การซ้อน Callback หลายชั้น) ด้วยวิธีที่สง่างาม ทำให้ Code ดู Sequential แต่ทำงานแบบ Callback จริงๆ
import gleam/result
import gleam/io
import gleam/string
// จำลองฟังก์ชันที่อาจ Fail
fn find_user(id: Int) -> Result(String, String) {
case id {
1 -> Ok("สมชาย")
2 -> Ok("สมหญิง")
_ -> Error("ไม่พบผู้ใช้ ID: " <> string.inspect(id))
}
}
fn find_email(name: String) -> Result(String, String) {
case name {
"สมชาย" -> Ok("somchai@example.com")
_ -> Error("ไม่พบอีเมลของ: " <> name)
}
}
fn send_email(email: String) -> Result(String, String) {
Ok("ส่งแล้วไปที่: " <> email)
}
pub fn main() {
// ❌ แบบไม่ใช้ use — ซ้อนกันน่าเวียนหัว
let old_result = result.try(find_user(1), fn(name) {
result.try(find_email(name), fn(email) {
send_email(email)
})
})
// ✅ แบบใช้ use — อ่านง่ายเหมือน Sequential Code
let new_result = {
use name <- result.try(find_user(1))
use email <- result.try(find_email(name))
use res <- result.try(send_email(email))
Ok(res)
}
case new_result {
Ok(message) -> io.println("✅ " <> message)
Error(err) -> io.println("❌ " <> err)
}
// → ✅ ส่งแล้วไปที่: somchai@example.com
}
ใน Gleam ไม่มี for loop หรือ while loop แบบ Imperative — การวนซ้ำทำได้ด้วย Recursion เท่านั้น ซึ่งสอดคล้องกับหลักการ Functional Programming
import gleam/io
import gleam/string
// Factorial — ตัวอย่างพื้นฐาน
pub fn factorial(n: Int) -> Int {
case n {
0 -> 1 // Base Case: หยุดวนซ้ำ
1 -> 1 // Base Case เพิ่มเติม
_ -> n * factorial(n - 1) // Recursive Case
}
}
// Fibonacci — ตัวอย่างคลาสสิก
pub fn fibonacci(n: Int) -> Int {
case n {
0 -> 0
1 -> 1
_ -> fibonacci(n - 1) + fibonacci(n - 2)
}
}
// นับผลรวมของ List
pub fn sum(items: List(Int)) -> Int {
case items {
[] -> 0 // List ว่าง = ผลรวม 0
[head, ..tail] -> head + sum(tail) // บวกหัวกับผลรวมหาง
}
}
pub fn main() {
io.println("5! = " <> string.inspect(factorial(5))) // → 120
io.println("fib(10) = " <> string.inspect(fibonacci(10))) // → 55
io.println("sum = " <> string.inspect(sum([1, 2, 3, 4, 5]))) // → 15
}
สำหรับ Fibonacci แบบ Naive:
ซึ่งช้ามากสำหรับค่า n ที่ใหญ่ — จึงต้องใช้ Tail Call Optimization
TCO คือเทคนิคที่ Compiler/VM แปลง Tail Recursion (การเรียกตัวเองเป็นคำสั่งสุดท้าย) ให้กลายเป็น Loop อัตโนมัติ ช่วยประหยัด Stack Frame
import gleam/io
import gleam/list
import gleam/string
// ❌ Fibonacci แบบ Naive — O(2^n) และใช้ Stack มาก
pub fn fib_naive(n: Int) -> Int {
case n {
0 -> 0
1 -> 1
_ -> fib_naive(n - 1) + fib_naive(n - 2)
// ไม่ใช่ Tail Call เพราะยังต้องบวกหลังจากเรียกตัวเอง
}
}
// ✅ Fibonacci แบบ Tail-Recursive — O(n) และไม่ใช้ Stack เพิ่ม
pub fn fib_tail(n: Int) -> Int {
fib_helper(n, 0, 1) // เริ่มด้วย Accumulator
}
fn fib_helper(n: Int, a: Int, b: Int) -> Int {
case n {
0 -> a
_ -> fib_helper(n - 1, b, a + b) // Tail Call!
}
}
// ✅ Sum แบบ Tail-Recursive
pub fn sum_tail(items: List(Int)) -> Int {
go_sum(items, 0) // Accumulator เริ่มที่ 0
}
fn go_sum(items: List(Int), acc: Int) -> Int {
case items {
[] -> acc
[head, ..tail] -> go_sum(tail, acc + head) // Tail Call!
}
}
pub fn main() {
// TCO ทำให้รันได้แม้ n ใหญ่มากๆ โดยไม่ Stack Overflow
io.println("fib(40) = " <> string.inspect(fib_tail(40))) // → 102334155
io.println("fib(100) = " <> string.inspect(fib_tail(100)))
io.println("sum(1..100) = " <> string.inspect(sum_tail(list.range(1, 100))))
// → sum(1..100) = 5050
}
import gleam/io
import gleam/list
import gleam/int
import gleam/string
pub fn main() {
let student_scores = [55, 78, 42, 91, 63, 87, 34, 76]
// list.map — แปลงแต่ละสมาชิก
let letter_grades =
student_scores
|> list.map(fn(score) {
case score {
x if x >= 80 -> "A"
x if x >= 70 -> "B"
x if x >= 60 -> "C"
x if x >= 50 -> "D"
_ -> "F"
}
})
io.debug(letter_grades)
// → ["D", "B", "F", "A", "C", "A", "F", "B"]
// list.filter — กรองตามเงื่อนไข
let passed = list.filter(student_scores, fn(x) { x >= 50 })
io.debug(passed)
// → [55, 78, 91, 63, 87, 76]
// list.fold — รวมเป็นค่าเดียว (Accumulator)
let total = list.fold(student_scores, 0, int.add)
let count = list.length(student_scores)
let average = int.to_float(total) /. int.to_float(count)
io.println("คะแนนเฉลี่ย: " <> string.inspect(average))
// → คะแนนเฉลี่ย: 65.75
// list.sort — เรียงลำดับ
let sorted = list.sort(student_scores, int.compare)
io.debug(sorted)
// → [34, 42, 55, 63, 76, 78, 87, 91]
// list.zip — รวม 2 List เป็น Tuple
let names = ["ก", "ข", "ค"]
let scores = [80, 70, 90]
let paired = list.zip(names, scores)
io.debug(paired)
// → [#("ก", 80), #("ข", 70), #("ค", 90)]
// list.flatten — Flatten nested list
let nested_data = [[1, 2], [3, 4], [5]]
let flat = list.flatten(nested_data)
io.debug(flat)
// → [1, 2, 3, 4, 5]
}
import gleam/dict
import gleam/set
import gleam/io
import gleam/string
pub fn main() {
// === Dict (Key-Value Store) ===
// สร้าง Dict จาก List of Tuples
let inventory =
dict.from_list([
#("แอปเปิล", 50),
#("ส้ม", 30),
#("มะม่วง", 80),
])
// เพิ่ม/แก้ไข
let updated_inventory = dict.insert(inventory, "กล้วย", 100)
// ดึงค่า
case dict.get(updated_inventory, "แอปเปิล") {
Ok(quantity) -> io.println("แอปเปิล: " <> string.inspect(quantity) <> " ลูก")
Error(_) -> io.println("ไม่พบสินค้า")
}
// → แอปเปิล: 50 ลูก
// วนทุก Entry
dict.each(updated_inventory, fn(item, quantity) {
io.println(item <> " → " <> string.inspect(quantity))
})
// === Set (ไม่ซ้ำ) ===
let class_a_students = set.from_list(["สมชาย", "สมหญิง", "วิชัย"])
let class_b_students = set.from_list(["สมหญิง", "วิชัย", "ประเสริฐ"])
// ร่วมกัน (Union)
let all_students = set.union(class_a_students, class_b_students)
io.debug(set.to_list(all_students))
// → ["สมชาย", "สมหญิง", "วิชัย", "ประเสริฐ"]
// ตัดกัน (Intersection)
let in_both = set.intersection(class_a_students, class_b_students)
io.debug(set.to_list(in_both))
// → ["สมหญิง", "วิชัย"]
// ผลต่าง (Difference)
let only_class_a = set.difference(class_a_students, class_b_students)
io.debug(set.to_list(only_class_a))
// → ["สมชาย"]
}
Iterator (Yielder) ใน Gleam ใช้สำหรับ Lazy Evaluation — คำนวณค่าเฉพาะเมื่อจำเป็น เหมาะสำหรับชุดข้อมูลขนาดใหญ่หรืออนันต์
import gleam/iterator
import gleam/io
import gleam/string
pub fn main() {
// สร้าง Iterator แบบอนันต์ (Infinite Sequence)
let natural_numbers = iterator.range(1, 1_000_000)
// ดึงแค่ 10 ตัวแรก — ไม่ต้องสร้าง List ล้านตัว!
let first_ten =
natural_numbers
|> iterator.take(10)
|> iterator.to_list
io.debug(first_ten)
// → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// Iterator Pipeline — Lazy: คำนวณเฉพาะเมื่อต้องการ
let result =
iterator.range(1, 100)
|> iterator.filter(fn(x) { x % 2 == 0 }) // กรองเลขคู่
|> iterator.map(fn(x) { x * x }) // ยกกำลังสอง
|> iterator.take(5) // เอาแค่ 5 ตัว
|> iterator.to_list
io.debug(result)
// → [4, 16, 36, 64, 100]
// สร้าง Custom Iterator
let fibonacci_seq =
iterator.unfold(#(0, 1), fn(state) {
let #(a, b) = state
iterator.Next(element: a, accumulator: #(b, a + b))
})
let fibonacci_20 =
fibonacci_seq
|> iterator.take(10)
|> iterator.to_list
io.debug(fibonacci_20)
// → [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
}
นี่คือหัวใจสำคัญของ Type-safe Error Handling ใน Gleam — แทนที่จะใช้ null หรือ throw Exception Gleam ใช้ Type พิเศษสองตัว:
Option(a) — ค่าที่อาจมีหรือไม่มีก็ได้ (Some(value) หรือ None)Result(value, error) — การดำเนินการที่อาจสำเร็จ (Ok(value)) หรือล้มเหลว (Error(reason))import gleam/option.{type Option, Some, None}
import gleam/result
import gleam/io
import gleam/int
import gleam/list
import gleam/string
// ฟังก์ชันที่ใช้ Option
fn find_maximum(items: List(Int)) -> Option(Int) {
case list.sort(items, int.compare) |> list.last {
Ok(value) -> Some(value)
Error(_) -> None // List ว่าง
}
}
// ฟังก์ชันที่ใช้ Result
fn divide(dividend: Float, divisor: Float) -> Result(Float, String) {
case divisor {
0.0 -> Error("ไม่สามารถหารด้วย 0 ได้!")
_ -> Ok(dividend /. divisor)
}
}
fn parse_number(text: String) -> Result(Int, String) {
case int.parse(text) {
Ok(n) -> Ok(n)
Error(_) -> Error("'" <> text <> "' ไม่ใช่ตัวเลข")
}
}
pub fn main() {
// ทดสอบ Option
case find_maximum([3, 1, 4, 1, 5, 9]) {
Some(value) -> io.println("ค่าสูงสุด: " <> string.inspect(value))
None -> io.println("ไม่มีข้อมูล")
}
// → ค่าสูงสุด: 9
case find_maximum([]) {
Some(value) -> io.println("ค่าสูงสุด: " <> string.inspect(value))
None -> io.println("รายการว่าง ไม่มีค่าสูงสุด")
}
// → รายการว่าง ไม่มีค่าสูงสุด
// ทดสอบ Result
case divide(10.0, 3.0) {
Ok(result) -> io.println("ผล: " <> string.inspect(result))
Error(err) -> io.println("Error: " <> err)
}
// → ผล: 3.3333333333333335
case divide(10.0, 0.0) {
Ok(result) -> io.println("ผล: " <> string.inspect(result))
Error(err) -> io.println("Error: " <> err)
}
// → Error: ไม่สามารถหารด้วย 0 ได้!
// Result Chaining ด้วย result.try
let computation = {
use x <- result.try(parse_number("42"))
use y <- result.try(parse_number("8"))
Ok(x + y)
}
io.debug(computation) // → Ok(50)
let failure = {
use x <- result.try(parse_number("42"))
use y <- result.try(parse_number("abc")) // จะ Error ที่นี่
Ok(x + y)
}
io.debug(failure) // → Error("'abc' ไม่ใช่ตัวเลข")
}
panic |
assert |
|
|---|---|---|
| ใช้เมื่อ | Unreachable code, Bug ที่ไม่ควรเกิด | ตรวจสอบ Invariant ระหว่าง Development |
| ผล | หยุด Process ทันที | หยุด Process ถ้า Condition เป็น False |
| ข้อความ | ระบุได้ | ไม่ระบุ (แสดง Expression ที่ Fail) |
| แนะนำให้ใช้ | น้อยมาก | ระหว่าง Debug เท่านั้น |
import gleam/io
import gleam/string
fn process_status(status: Int) -> String {
case status {
1 -> "กำลังดำเนินการ"
2 -> "เสร็จสิ้น"
3 -> "ยกเลิก"
_ -> panic as "สถานะที่ไม่รู้จัก: " <> string.inspect(status)
// ใช้ panic เมื่อ "ถ้าถึงตรงนี้แสดงว่า Bug แน่นอน"
}
}
fn validate_age(age: Int) -> String {
let assert True = age >= 0 // จะ Crash ถ้าอายุติดลบ
"อายุ " <> string.inspect(age) <> " ปี"
}
pub fn main() {
io.println(process_status(1)) // → กำลังดำเนินการ
io.println(validate_age(20)) // → อายุ 20 ปี
// validate_age(-1) จะ Crash!
}
Bit Arrays คือจุดแข็งพิเศษของ BEAM ที่ Gleam รองรับ — ใช้สำหรับ Binary Protocol, Parsing, Cryptography
import gleam/bit_array
import gleam/io
import gleam/string
pub fn main() {
// สร้าง Bit Array จาก String
let data = bit_array.from_string("Hello, Gleam!")
io.debug(data)
// → <<72, 101, 108, 108, 111, 44, 32, 71, 108, 101, 97, 109, 33>>
// แปลงกลับเป็น String
case bit_array.to_string(data) {
Ok(text) -> io.println("ข้อความ: " <> text)
Error(_) -> io.println("ไม่ใช่ UTF-8 ที่ถูกต้อง")
}
// → ข้อความ: Hello, Gleam!
// Bit Pattern Matching — ทรงพลังมาก!
let packet = <<1:8, 255:8, 42:16>>
case packet {
<<packet_type:8, size:8, payload:16>> -> {
io.println("ประเภท: " <> string.inspect(packet_type))
io.println("ขนาด: " <> string.inspect(size))
io.println("ข้อมูล: " <> string.inspect(payload))
}
_ -> io.println("รูปแบบไม่ถูกต้อง")
}
// ตัวอย่าง: จำลอง Parse TCP Header อย่างง่าย
let tcp_header = <<0x00:8, 0x50:8, 0x01:8, 0xBB:8>>
case tcp_header {
<<src_port:16, dst_port:16>> -> {
io.println("Source Port: " <> string.inspect(src_port))
io.println("Dest Port: " <> string.inspect(dst_port))
}
_ -> io.println("Header ไม่ถูกต้อง")
}
// → Source Port: 80
// → Dest Port: 443
}
// ต้องเพิ่ม dependency ก่อน:
// gleam add gleam_json
import gleam/json
import gleam/dynamic
import gleam/io
// โครงสร้างข้อมูล
pub type Student {
Student(
id: String,
name: String,
score: Float,
)
}
// Encoder: แปลง Struct → JSON
fn encode_student(student: Student) -> json.Json {
json.object([
#("id", json.string(student.id)),
#("name", json.string(student.name)),
#("score", json.float(student.score)),
])
}
// Decoder: แปลง JSON → Struct
fn decode_student(
data: dynamic.Dynamic,
) -> Result(Student, List(dynamic.DecodeError)) {
dynamic.decode3(
Student,
dynamic.field("id", dynamic.string),
dynamic.field("name", dynamic.string),
dynamic.field("score", dynamic.float),
)(data)
}
pub fn main() {
// Encode เป็น JSON String
let student = Student(id: "64001", name: "สมชาย", score: 85.5)
let json_string = json.to_string(encode_student(student))
io.println(json_string)
// → {"id":"64001","name":"สมชาย","score":85.5}
// Decode จาก JSON String
let json_input = "{\"id\":\"64002\",\"name\":\"สมหญิง\",\"score\":92.0}"
case json.decode(json_input, decode_student) {
Ok(student2) -> io.println("Decode สำเร็จ: " <> student2.name)
Error(_) -> io.println("Decode ล้มเหลว")
}
// → Decode สำเร็จ: สมหญิง
}
Actor Model คือรากฐานของ Concurrency บน BEAM — แทนที่จะใช้ Shared Memory ที่เสี่ยงต่อ Race Condition, Actor แต่ละตัวจะสื่อสารกันผ่านการ ส่ง Message เท่านั้น
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#3c3836', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#504945', 'lineColor': '#fb4934', 'background': '#282828', 'mainBkg': '#3c3836', 'clusterBkg': '#1d2021', 'titleColor': '#fabd2f', 'fontFamily': 'monospace'}}
}%%
sequenceDiagram
participant A as Process A
(ผู้ส่ง)
participant MB as Mailbox B
(กล่องจดหมาย)
participant B as Process B
(ผู้รับ)
participant C as Process C
A->>MB: ส่ง Message{"สวัสดี"}
A->>C: ส่ง Message ไปพร้อมกัน (Async)
Note over A: ทำงานต่อได้ทันที
MB->>B: B รับและประมวลผล
B-->>A: ตอบกลับ (ถ้าต้องการ)
import gleam/erlang/process
import gleam/io
pub fn main() {
// สร้าง Process ใหม่ด้วย spawn
let pid = process.start(
fn() {
io.println("สวัสดีจาก Process ใหม่!")
process.sleep(1000)
io.println("Process ใหม่เสร็จสิ้น")
},
False, // ไม่ Link กับ Parent
)
io.println("Process หลักทำงานต่อได้เลย")
process.sleep(2000) // รอให้ Process ลูกทำงานเสร็จ
}
// gleam add gleam_otp
import gleam/otp/actor
import gleam/otp/task
import gleam/erlang/process
import gleam/io
import gleam/string
// กำหนดประเภท Message
pub type CounterMsg {
Increment(amount: Int)
Decrement(amount: Int)
GetValue(reply_to: process.Subject(Int))
Stop
}
// Actor Handler
fn handle_message(
msg: CounterMsg,
state: Int,
) -> actor.Next(CounterMsg, Int) {
case msg {
Increment(n) -> actor.continue(state + n)
Decrement(n) -> actor.continue(state - n)
GetValue(reply_ch) -> {
process.send(reply_ch, state)
actor.continue(state)
}
Stop -> actor.Stop(process.Normal)
}
}
pub fn main() {
// เริ่ม Actor
let assert Ok(counter) = actor.start(0, handle_message)
// ส่ง Message
process.send(counter, Increment(10))
process.send(counter, Increment(5))
process.send(counter, Decrement(3))
// รับค่ากลับ
let current_value = process.call(counter, GetValue, 1000)
io.println("ค่าปัจจุบัน: " <> string.inspect(current_value))
// → ค่าปัจจุบัน: 12
// Task — สำหรับงานแบบ One-shot
let task1 = task.async(fn() {
process.sleep(100)
"ผลจากงาน 1"
})
let task2 = task.async(fn() {
process.sleep(50)
"ผลจากงาน 2"
})
// รอผล (รันพร้อมกัน!)
let result1 = task.await(task1, 1000)
let result2 = task.await(task2, 1000)
io.println(string.inspect(result1))
io.println(string.inspect(result2))
}
Supervisor คือ Process พิเศษที่คอยดูแล Child Process — ถ้า Child พัง Supervisor จะ Restart ให้อัตโนมัติ
import gleam/otp/supervisor
import gleam/otp/actor
import gleam/io
// Worker ที่อาจล้มเหลว
fn start_worker(name: String) {
actor.start(
name,
fn(msg, state) {
io.println("[" <> state <> "] รับ: " <> msg)
actor.continue(state)
},
)
}
pub fn main() {
// สร้าง Supervision Tree
let children = [
supervisor.worker(fn(_) { start_worker("Worker-A") }),
supervisor.worker(fn(_) { start_worker("Worker-B") }),
]
supervisor.start(fn(children) {
supervisor.init(children, supervisor.OneForOne)
// OneForOne: ถ้า Worker ตัวหนึ่งพัง Restart เฉพาะตัวนั้น
// OneForAll: ถ้าตัวหนึ่งพัง Restart ทั้งหมด
// RestForOne: ถ้าตัวหนึ่งพัง Restart ตัวนั้นและตัวที่ Start หลังมัน
})
}
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#3c3836', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#504945', 'lineColor': '#8ec07c', 'background': '#282828', 'mainBkg': '#3c3836', 'clusterBkg': '#1d2021', 'titleColor': '#fabd2f', 'fontFamily': 'monospace'}}
}%%
graph TD
SUP["🛡️ Supervisor"]
W1["Worker A"]
W2["Worker B 💥"]
W3["Worker C"]
SUP --> W1
SUP --> W2
SUP --> W3
subgraph OneForOne["one_for_one: Restart แค่ตัวที่พัง"]
R2["🔄 Restart Worker B เท่านั้น"]
end
subgraph OneForAll["one_for_all: Restart ทั้งหมด"]
R_ALL["🔄 Restart A, B, C ทั้งหมด"]
end
W2 -- "พัง!" --> OneForOne
W2 -- "พัง!" --> OneForAll
// การเรียกใช้ Erlang/Elixir Library ผ่าน FFI
// ไฟล์: src/crypto_ffi.gleam
// ประกาศ External Function
@external(erlang, "crypto", "hash")
pub fn erlang_hash(algorithm: Atom, data: BitArray) -> BitArray
// ไฟล์: src/my_module.gleam
import gleam/io
import gleam/bit_array
pub fn main() {
// เรียกใช้ :crypto.hash(:sha256, data) จาก Erlang
let data = bit_array.from_string("Hello, Gleam!")
// let hash = erlang_hash(atom.create_from_string("sha256"), data)
io.println("เรียก Erlang crypto ได้โดยตรง!")
}
# เพิ่ม Dependencies
gleam add mist wisp gleam_http
// src/my_api.gleam
import mist
import wisp.{type Request, type Response}
import gleam/io
import gleam/json
import gleam/erlang/process
// Handler สำหรับ Router
pub fn handle_request(req: Request) -> Response {
case wisp.path_segments(req) {
// GET /
[] -> {
wisp.response(200)
|> wisp.set_header("content-type", "text/plain")
|> wisp.string_body("สวัสดี Gleam Web! 🎉")
}
// GET /api/students
["api", "students"] -> {
let students =
json.array(
[
json.object([#("id", json.int(1)), #("name", json.string("สมชาย"))]),
json.object([#("id", json.int(2)), #("name", json.string("สมหญิง"))]),
],
fn(x) { x },
)
wisp.response(200)
|> wisp.set_header("content-type", "application/json")
|> wisp.string_body(json.to_string(students))
}
// 404 Not Found
_ -> wisp.not_found()
}
}
pub fn main() {
wisp.configure_logger()
let assert Ok(_) = mist.run_service(
mist.new(handle_request)
|> mist.port(8080),
)
io.println("เซิร์ฟเวอร์รันที่ http://localhost:8080")
process.sleep_forever()
}
Lustre คือ Framework สำหรับสร้าง UI บน JavaScript Target ใช้ Elm Architecture (Model-View-Update)
// src/app.gleam — Lustre App
import lustre
import lustre/element.{type Element}
import lustre/element/html
import lustre/event
import lustre/attribute
import gleam/string
// Model
pub type Model {
Model(count: Int, text: String)
}
fn init(_flags) -> Model {
Model(count: 0, text: "สวัสดี Lustre!")
}
// Messages
pub type Msg {
Increment
Decrement
Reset
}
// Update — Pure Function
fn update(model: Model, msg: Msg) -> Model {
case msg {
Increment -> Model(..model, count: model.count + 1)
Decrement -> Model(..model, count: model.count - 1)
Reset -> Model(..model, count: 0)
}
}
// View — Pure Function
fn view(model: Model) -> Element(Msg) {
html.div([attribute.class("container")], [
html.h1([], [element.text(model.text)]),
html.p([], [element.text("ค่าปัจจุบัน: " <> string.inspect(model.count))]),
html.div([], [
html.button([event.on_click(Decrement)], [element.text("−")]),
html.button([event.on_click(Reset)], [element.text("Reset")]),
html.button([event.on_click(Increment)], [element.text("+")]),
]),
])
}
pub fn main() {
let app = lustre.simple(init, update, view)
let assert Ok(_) = lustre.start(app, "#app", Nil)
}
# Build สำหรับ JavaScript
gleam build --target javascript
# ผลลัพธ์อยู่ใน build/dev/javascript/
# สามารถ import ในโปรเจกต์ JS/TS ได้เลย
// ไฟล์ที่รันได้บน Deno
import gleam/io
@external(javascript, "./runtime.mjs", "getCurrentTime")
fn get_current_time() -> String
pub fn main() {
io.println("รันบน JavaScript!")
io.println("เวลา: " <> get_current_time())
}
// test/my_app_test.gleam
import gleeunit
import gleeunit/should
// ฟังก์ชันที่ต้องการทดสอบ (import จาก src/)
import my_app
pub fn main() {
gleeunit.main()
}
// Test Function — ชื่อต้องลงท้ายด้วย _test
pub fn factorial_test() {
my_app.factorial(0) |> should.equal(1)
my_app.factorial(1) |> should.equal(1)
my_app.factorial(5) |> should.equal(120)
my_app.factorial(10) |> should.equal(3_628_800)
}
pub fn fibonacci_test() {
my_app.fibonacci(0) |> should.equal(0)
my_app.fibonacci(1) |> should.equal(1)
my_app.fibonacci(10) |> should.equal(55)
}
pub fn sum_test() {
my_app.sum([]) |> should.equal(0)
my_app.sum([1]) |> should.equal(1)
my_app.sum([1, 2, 3]) |> should.equal(6)
my_app.sum([1, 2, 3, 4, 5]) |> should.equal(15)
}
// ทดสอบ Error Case
pub fn divide_test() {
my_app.divide(10.0, 2.0) |> should.equal(Ok(5.0))
my_app.divide(10.0, 0.0) |> should.be_error
}
Property-Based Testing คือการทดสอบโดยกำหนด คุณสมบัติ (Property) ที่ต้องเป็นจริงเสมอ แล้วให้ Framework สร้างตัวอย่างทดสอบให้อัตโนมัติ
// gleam add qcheck
import qcheck
import gleeunit/should
import gleam/list
import gleam/int
// Property: reverse ของ reverse ต้องได้ List เดิม
pub fn reverse_involution_test() {
use items <- qcheck.given(qcheck.list(qcheck.int()))
items
|> list.reverse
|> list.reverse
|> should.equal(items)
}
// Property: sum ต้องเท่ากับการบวกทีละตัว
pub fn sum_commutative_test() {
use a <- qcheck.given(qcheck.list(qcheck.int()))
use b <- qcheck.given(qcheck.list(qcheck.int()))
let total_combined = list.flatten([a, b]) |> list.fold(0, int.add)
let total_separate = list.fold(a, 0, int.add) + list.fold(b, 0, int.add)
total_combined |> should.equal(total_separate)
}
// Property: sort แล้ว length ต้องเท่าเดิม
pub fn sort_preserves_length_test() {
use items <- qcheck.given(qcheck.list(qcheck.int()))
let length_before = list.length(items)
let length_after = items |> list.sort(int.compare) |> list.length
length_before |> should.equal(length_after)
}
my_project/
├── src/
│ ├── my_project.gleam # Entry point
│ ├── domain/
│ │ ├── student.gleam # Business logic
│ │ └── course.gleam
│ └── infrastructure/
│ ├── database.gleam
│ └── http_client.gleam
└── test/
├── my_project_test.gleam # Main test runner
├── domain/
│ ├── student_test.gleam # Unit tests
│ └── course_test.gleam
└── integration/
└── api_test.gleam # Integration tests
# Build สำหรับ Erlang/OTP (Default)
gleam build --target erlang
# Build สำหรับ JavaScript
gleam build --target javascript
# รันบน Erlang
gleam run --target erlang
# รันบน JavaScript (Node.js)
gleam run --target javascript --runtime nodejs
# รันบน JavaScript (Deno)
gleam run --target javascript --runtime deno
# วิธีที่ 1: ใช้ escriptize (Erlang)
# เพิ่มใน gleam.toml:
# [target.erlang]
# escriptize = true
gleam build --target erlang
# ได้ไฟล์ build/erlang-shipment/ ที่พอร์ตไปรันได้
# วิธีที่ 2: ใช้ Deno compile (JavaScript)
gleam build --target javascript
deno compile --allow-all build/dev/javascript/my_app/my_app.mjs -o my_app
./my_app # รันได้เลยโดยไม่ต้อง Runtime!
# วิธีที่ 3: Erlang Escript
gleam export erlang-shipment
cd build/erlang-shipment
./entrypoint.sh run my_app
# Dockerfile สำหรับ Gleam Application (Multi-stage Build)
# Stage 1: Build
FROM hexpm/erlang:26.2.5.2-ubuntu-jammy-20240808 AS builder
# ติดตั้ง Gleam
RUN curl -fsSL https://github.com/gleam-lang/gleam/releases/latest/download/gleam-$(uname -m)-unknown-linux-musl.tar.gz \
| tar -xz -C /usr/local/bin
WORKDIR /app
COPY gleam.toml manifest.toml ./
RUN gleam deps download
COPY . .
RUN gleam export erlang-shipment
# Stage 2: Runtime (เล็กกว่า!)
FROM hexpm/erlang:26.2.5.2-ubuntu-jammy-20240808
WORKDIR /app
COPY --from=builder /app/build/erlang-shipment ./
EXPOSE 8080
ENTRYPOINT ["./entrypoint.sh"]
CMD ["run", "my_api"]
# docker-compose.yml
version: '3.8'
services:
api:
build: .
ports:
- "8080:8080"
environment:
- PORT=8080
- DATABASE_URL=postgres://localhost/mydb
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
# เตรียม Production Release
# 1. อัปเดต version ใน gleam.toml
# version = "1.0.0"
# 2. รัน Test ทั้งหมดก่อน
gleam test
# 3. Format Code
gleam format --check
# 4. Build สำหรับ Production
gleam build --target erlang
# 5. Export Erlang Shipment
gleam export erlang-shipment
# 6. Build Docker Image
docker build -t my-gleam-app:1.0.0 .
# 7. Push ไปยัง Registry
docker push my-registry.io/my-gleam-app:1.0.0
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#3c3836', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#504945', 'lineColor': '#b8bb26', 'background': '#282828', 'mainBkg': '#3c3836', 'clusterBkg': '#1d2021', 'titleColor': '#fabd2f', 'fontFamily': 'monospace'}}
}%%
flowchart LR
subgraph Dev["💻 Development"]
A["เขียน Code"] --> B["gleam check"]
B --> C["gleam test"]
C --> D["git push"]
end
subgraph CI["⚙️ CI/CD Pipeline"]
E["Trigger\n(GitHub Actions)"] --> F["gleam format --check"]
F --> G["gleam test"]
G --> H["docker build"]
H --> I["Push to Registry"]
end
subgraph Prod["🚀 Production"]
J["Pull Image"] --> K["Deploy\n(Zero-downtime)"]
K --> L["Health Check"]
L --> M["✅ Live!"]
end
D --> E
I --> J
Gleam คือการผสมผสานที่ลงตัวระหว่างความปลอดภัยของระบบ Type แบบ Static กับพลังของ BEAM VM ที่ผ่านการพิสูจน์มาหลายสิบปีในระบบโทรคมนาคมระดับ Carrier
| จุดเด่น | ผลลัพธ์ |
|---|---|
| Static Type + BEAM | ความปลอดภัยที่ Compile-time + Concurrency ระดับล้าน Process |
| Dual Target | Code ชุดเดียวรันได้ทั้ง Server (Erlang) และ Browser (JS) |
| Friendly Errors | Error message ที่ช่วยแนะนำ ลดเวลา Debug |
| Erlang/Elixir Interop | Ecosystem ขนาดใหญ่พร้อมใช้งาน |
| Simple Syntax | เรียนรู้ได้เร็ว ลด Cognitive Load |
| แหล่ง | URL | เนื้อหา |
|---|---|---|
| เว็บไซต์ทางการ | gleam.run | เอกสาร, Tour, Blog |
| Language Tour | tour.gleam.run | Interactive Tutorial |
| Package Registry | packages.gleam.run | ค้นหา Library |
| Stdlib Docs | hexdocs.pm/gleam_stdlib | เอกสาร Standard Library |
| GitHub | github.com/gleam-lang | Source Code |
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#3c3836', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#504945', 'lineColor': '#83a598', 'background': '#282828', 'mainBkg': '#3c3836', 'clusterBkg': '#1d2021', 'titleColor': '#fabd2f', 'fontFamily': 'monospace'}}
}%%
flowchart TD
A["🌱 เริ่มต้น\nLanguage Tour\ntour.gleam.run"] --> B["📖 ฝึกพื้นฐาน\nVariables, Types\nPattern Matching"]
B --> C["🔧 สร้าง Project แรก\nCLI Tool หรือ\nSimple API"]
C --> D{เลือกเส้นทาง}
D --> E["🖥️ Backend\nMist/Wisp\nOTP/Supervisor"]
D --> F["🌐 Frontend\nLustre\nJS Target"]
D --> G["⚡ Systems\nBit Arrays\nFFI กับ Erlang"]
E --> H["🚀 Production\nDocker, Release\nMonitoring"]
F --> H
G --> H
H --> I["🤝 Contribute\nOpen Source\nCommunity"]