การโปรแกรมภาษา Gleam: คู่มือฉบับสมบูรณ์

1. ปฐมบท: ทำความรู้จักกับ Gleam

1.1 Gleam คืออะไร?

Gleam คือภาษาโปรแกรมแบบ Functional Programming ที่ถูกออกแบบมาเพื่อทำงานบน BEAM Virtual Machine (Erlang VM) โดยให้ความสำคัญกับ Type Safety สูงสุด กล่าวคือ ข้อผิดพลาดส่วนใหญ่จะถูกตรวจพบในขั้นตอนการ Compile ก่อนที่โปรแกรมจะถูกรันจริง

ภาษา Gleam ถูกสร้างโดย Louis Pilfold และเปิดตัวเวอร์ชันเสถียร (v1.0) ในปี ค.ศ. 2024 โดยได้รับแรงบันดาลใจจากภาษา Rust ในแง่ของระบบ Type และความปลอดภัย และจากภาษา Elm ในแง่ของความเรียบง่ายและ Developer Experience ที่ดี

1.1.1 ปรัชญาหลักของ Gleam

1.1.2 ไทม์ไลน์วิวัฒนาการของ Gleam

%%{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

1.2 The Power of BEAM

BEAM (Bogdan/Björn's Erlang Abstract Machine) คือ Virtual Machine ที่ถูกออกแบบโดย Ericsson สำหรับระบบโทรคมนาคม ซึ่งต้องการ Uptime 99.9999999% (Nine nines) หรือ Downtime ไม่เกิน 31 มิลลิวินาทีต่อปี

1.2.1 คุณสมบัติเด่นของ BEAM

คุณสมบัติ คำอธิบาย ผลลัพธ์
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 โดยธรรมชาติ

1.2.2 สถาปัตยกรรม BEAM

%%{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

1.3 Why Gleam?

1.3.1 เปรียบเทียบกับภาษาอื่นบน BEAM

ด้าน 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

1.3.2 ทำไม Gleam ถึงน่าสนใจในปี 2024+


2. การเตรียมสภาพแวดล้อม (Professional DX)

2.1 Installation

2.1.1 ข้อกำหนดเบื้องต้น (Prerequisites)

ก่อนติดตั้ง Gleam ต้องมีสิ่งต่อไปนี้:

  1. Erlang/OTP 26+ — Runtime สำหรับ BEAM Target
  2. Rebar3 — Build Tool ของ Erlang (ติดตั้งพร้อม Erlang)
  3. Node.js 18+ (ถ้าต้องการ JavaScript Target)

2.1.2 การติดตั้งบนระบบต่างๆ

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

2.1.3 สร้างโปรเจกต์แรก

# สร้างโปรเจกต์ใหม่
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"

2.2 Editor Power-up

2.2.1 VS Code

  1. เปิด VS Code แล้วไปที่ Extensions (Ctrl+Shift+X)
  2. ค้นหา "Gleam" และติดตั้ง Extension ของ Louis Pilfold
  3. ตั้งค่า settings.json:
{
  "editor.formatOnSave": true,
  "[gleam]": {
    "editor.defaultFormatter": "Gleam.gleam",
    "editor.tabSize": 2
  }
}

2.2.2 Zed Editor

Zed รองรับ Gleam ผ่าน Language Server โดยอัตโนมัติ:

  1. เปิด Zed Settings (Cmd+,)
  2. เพิ่มใน settings.json:
{
  "languages": {
    "Gleam": {
      "format_on_save": "on",
      "tab_size": 2
    }
  }
}

2.3 Project Workflow

2.3.1 คำสั่งที่ใช้บ่อย

คำสั่ง หน้าที่ ใช้เมื่อไหร่
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

2.3.2 Workflow แนะนำ

%%{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"]

3. พื้นฐานและการโต้ตอบ (Foundations & I/O)

3.1 Variables & Immutability

ใน 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)
}

3.1.1 ทำไมต้อง Immutable?

%%{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

3.2 Basic Types & String

3.2.1 ประเภทข้อมูลพื้นฐาน

Type ตัวอย่าง คำอธิบาย
Int 42, -7, 0 จำนวนเต็ม (ไม่จำกัดขนาด)
Float 3.14, -0.5 ทศนิยม (IEEE 754 64-bit)
String "สวัสดี", "Hello" ข้อความ UTF-8
Bool True, False ค่าความจริง
Nil Nil ค่าว่าง (ไม่ใช่ null!)

3.2.2 การทำงานกับ String

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"]
}

3.3 Standard I/O

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)
}

3.4 Labelled Arguments

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)
}

3.5 Pipe Operator (|>)

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
}

3.5.1 หลักการทำงานของ Pipe

x |> f = f ( x ) x |> f |> g |> h = h ( g ( f ( x ) ) )

โดยที่ x คือค่าเริ่มต้น, f, g, h คือฟังก์ชันที่รับค่าจากซ้ายเป็น Argument แรก


4. ตรรกะและการออกแบบข้อมูล (Data Modeling)

4.1 Custom Types

Custom Types คือกระดูกสันหลังของ Gleam — ใช้ออกแบบโครงสร้างข้อมูลที่สื่อความหมายชัดเจนและปลอดภัย แบ่งเป็นสองรูปแบบหลัก:

4.1.1 Record Types

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))
}

4.1.2 Variant Types (Algebraic Data Types)

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)
}

4.2 Pattern Matching

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
}

4.2.1 Pattern Matching ขั้นสูง

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
}

4.3 Functions & Anonymous Functions

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]
}

4.4 Use Expression

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
}

5. การวนซ้ำและโครงสร้างข้อมูล (Recursion & Data Structures)

5.1 Recursive Functions

ใน 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
}

5.1.1 การวิเคราะห์ความซับซ้อน (Complexity)

T(n) = T(n-1) + O(1) = O(n)

สำหรับ Fibonacci แบบ Naive:

T(n) = T(n-1) + T(n-2) + O(1) = O(2n)

ซึ่งช้ามากสำหรับค่า n ที่ใหญ่ — จึงต้องใช้ Tail Call Optimization


5.2 Tail Call Optimization (TCO)

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
}

5.2.1 เปรียบเทียบการใช้ Memory

Stack Usage (Normal Recursion) = O(n) Stack Usage (Tail Recursion + TCO) = O(1)

5.3 List Processing

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]
}

5.4 Dict & Set

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))
  // → ["สมชาย"]
}

5.5 Iterator / Yielder

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]
}

6. ความปลอดภัยและการจัดการข้อผิดพลาด (Robustness)

6.1 Option & Result

นี่คือหัวใจสำคัญของ Type-safe Error Handling ใน Gleam — แทนที่จะใช้ null หรือ throw Exception Gleam ใช้ Type พิเศษสองตัว:

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' ไม่ใช่ตัวเลข")
}

6.2 Panic vs Assert

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!
}

6.3 Bit Arrays

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
}

6.4 JSON Handling

// ต้องเพิ่ม 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 สำเร็จ: สมหญิง
}

7. การทำงานขนาน (Concurrency & OTP)

7.1 Actor Model

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: ตอบกลับ (ถ้าต้องการ)

7.1.1 Process พื้นฐาน

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 ลูกทำงานเสร็จ
}

7.2 gleam_otp: Subject & Task

// 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))
}

7.3 gleam_otp: Supervisor

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 หลังมัน
  })
}

7.3.1 Supervision Strategy

%%{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

7.4 Interoperability (FFI)

// การเรียกใช้ 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 ได้โดยตรง!")
}

8. เว็บและการเชื่อมต่อ (Web Development)

8.1 HTTP Server ด้วย Mist/Wisp

# เพิ่ม 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()
}

8.2 Lustre (Frontend Framework)

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)
}

8.3 JavaScript Target

# 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())
}

9. การทดสอบ (Testing)

9.1 Testing with Gleeunit

// 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
}

9.2 Property-Based Testing

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)
}

9.3 Test Organization

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

10. การ Build และ Deployment (To Production)

10.1 Compiling Targets

# 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

10.2 Single Executable

# วิธีที่ 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

10.3 Docker & Container

# 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

10.4 Production Release

# เตรียม 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

10.4.1 Deployment Pipeline

%%{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

11. สรุปและก้าวต่อไป

11.1 บทสรุป

Gleam คือการผสมผสานที่ลงตัวระหว่างความปลอดภัยของระบบ Type แบบ Static กับพลังของ BEAM VM ที่ผ่านการพิสูจน์มาหลายสิบปีในระบบโทรคมนาคมระดับ Carrier

11.1.1 จุดเด่นที่ทำให้ Gleam แตกต่าง

จุดเด่น ผลลัพธ์
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

11.1.2 เหมาะกับงานประเภทใด?


11.2 แหล่งเรียนรู้เพิ่มเติมและ Community

11.2.1 แหล่งเรียนรู้ทางการ

แหล่ง 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

11.2.2 Community

11.2.3 แนวทางการเรียนรู้ต่อ

%%{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"]