/static/codemoomoo.png

OCaml 5 สำหรับการพัฒนาระบบ IT: Memory Safety, Concurrency และ Multicore

OCaml 5 for IT Systems Development: Memory Safety, Concurrency, and Multicore

OCaml 5 คือการปฏิวัติครั้งใหญ่ของภาษา OCaml ที่เปิดประตูสู่การเขียนโปรแกรมแบบ multicore parallelism อย่างแท้จริง พร้อมด้วย Effect Handlers ที่เป็น algebraic effects ระดับภาษา ทำให้ OCaml กลายเป็นหนึ่งในภาษาที่เหมาะสมที่สุดสำหรับงาน systems programming ที่ต้องการทั้ง type safety, performance และ concurrency


Part 1 — Why OCaml 5 for Systems Development

ส่วนนี้อธิบายเหตุผลที่นักพัฒนาระบบควรพิจารณา OCaml 5 เป็นเครื่องมือหลัก พร้อมเปรียบเทียบกับภาษายอดนิยมอื่น ๆ และวิธีการตั้งค่าสภาพแวดล้อมการพัฒนาให้พร้อมใช้งานในระดับ production


1. OCaml 5 ในโลก Systems Programming

OCaml 5 in the Systems Programming Landscape

OCaml (Objective Categorical Abstract Machine Language) เป็นภาษาตระกูล ML (Meta Language) ที่พัฒนาโดย INRIA ประเทศฝรั่งเศสตั้งแต่ปี 1996 ผสมผสานแนวคิด functional programming, object-oriented programming และ imperative programming เข้าด้วยกัน การเปิดตัว OCaml 5.0 ในเดือนธันวาคม 2022 ถือเป็นจุดเปลี่ยนสำคัญที่นำ Multicore Parallelism และ Effect Handlers เข้ามาในภาษา ซึ่งทำให้ OCaml ก้าวเข้าสู่ยุคใหม่ของ systems programming อย่างเต็มตัว

1.1 Timeline ของ OCaml และการเข้าสู่ยุค Multicore

%%{init: {'theme':'base', 'themeVariables': {
  'primaryColor':'#3c3836',
  'primaryTextColor':'#ebdbb2',
  'primaryBorderColor':'#928374',
  'lineColor':'#a89984',
  'secondaryColor':'#504945',
  'tertiaryColor':'#282828',
  'background':'#282828',
  'mainBkg':'#3c3836',
  'secondBkg':'#504945',
  'clusterBkg':'#32302f',
  'clusterBorder':'#d79921'
}}}%%
flowchart TD
    subgraph Era1["ยุคบุกเบิก (Pioneer Era) 1996-2005"]
        A1["1996: OCaml 1.00
เปิดตัวครั้งแรก"] --> A2["2003: OCaml 3.07
Polymorphic Variants"] A2 --> A3["2005: Industrial Adoption
Jane Street, Citrix"] end subgraph Era2["ยุคพัฒนา (Growth Era) 2006-2015"] B1["2012: OCaml 4.00
GADTs, First-class modules"] --> B2["2013: opam
Package manager"] B2 --> B3["2014: Dune/Jbuilder
Build system"] B3 --> B4["2015: MirageOS
Unikernel OS"] end subgraph Era3["ยุค Multicore (Multicore Era) 2016-2022"] C1["2016: Multicore OCaml
เริ่มวิจัยที่ OCaml Labs"] --> C2["2019: Effect Handlers
Algebraic Effects prototype"] C2 --> C3["2022: OCaml 5.0
Multicore + Effects!"] end subgraph Era4["ยุคปัจจุบัน (Modern Era) 2023-ปัจจุบัน"] D1["2023: OCaml 5.1
ปรับปรุง GC"] --> D2["2024: OCaml 5.2
Eio maturation"] D2 --> D3["2025: OCaml 5.3+
Production-ready"] end Era1 --> Era2 Era2 --> Era3 Era3 --> Era4 style Era1 fill:#32302f,stroke:#458588,color:#83a598 style Era2 fill:#32302f,stroke:#98971a,color:#b8bb26 style Era3 fill:#32302f,stroke:#d79921,color:#fabd2f style Era4 fill:#32302f,stroke:#cc241d,color:#fb4934

1.2 เปรียบเทียบ OCaml 5 กับ Rust, Go, C++ ในมุม Systems

การเลือกภาษาสำหรับ systems programming เป็นการตัดสินใจที่ส่งผลต่อ architecture, performance และ maintainability ในระยะยาว ตารางต่อไปนี้เปรียบเทียบคุณสมบัติหลักของภาษาสี่ภาษา

คุณสมบัติ (Feature) OCaml 5 Rust Go C++
Memory Management Garbage Collector (GC) Ownership + Borrowing Garbage Collector (GC) Manual + RAII
Memory Safety ปลอดภัย (ยกเว้น FFI) ปลอดภัยตาม type system ปลอดภัย (ยกเว้น unsafe) ไม่ปลอดภัยโดย default
Type System Hindley-Milner + Effects Affine types + Lifetimes Structural (limited generics) Template metaprogramming
Type Inference ครอบคลุมเกือบทั้งหมด (global) Local inference Local inference Limited (auto)
Concurrency Model Domains + Effects + Eio async/await + Send/Sync Goroutines + channels std::thread + coroutines
True Parallelism ได้ (OCaml 5) ได้ ได้ ได้
Compile Speed เร็วมาก ช้ามาก เร็วมาก ช้า
Runtime Size เล็ก (~500 KB) ไม่มี runtime กลาง (~2 MB) ไม่มี runtime
FFI Overhead ต่ำ (คล้าย C) ต่ำมาก (zero-cost) สูง (cgo) ไม่มี
GC Pause มี (generational, ~1-10ms) ไม่มี มี (sub-ms) ไม่มี
Pattern Matching รองรับเต็มรูปแบบ รองรับเต็มรูปแบบ ไม่รองรับ (มี switch) ไม่รองรับ (มี if constexpr)
Algebraic Data Types รองรับ (variants + records) รองรับ (enum + struct) ไม่รองรับ (interface{}) ไม่รองรับ (variant C++17)
Null Safety ไม่มี null (option type) ไม่มี null (Option) มี nil มี nullptr
Learning Curve ชันปานกลาง ชันมาก ต่ำ ชันสุด
Ecosystem Size กลาง (~4,500 packages) ใหญ่ (~150,000 crates) ใหญ่มาก ใหญ่มาก

1.3 จุดแข็งของ OCaml 5 สำหรับ Systems Work

1.3.1 Strong Static Typing with Full Inference

OCaml มี type system ที่ทรงพลังระดับเดียวกับ Haskell โดยสามารถอนุมาน (infer) ชนิดข้อมูลของตัวแปรและฟังก์ชันได้โดยไม่ต้องเขียน type annotation ให้ครบ ตัวอย่าง

(* Compiler อนุมานว่า f : int -> int -> int โดยอัตโนมัติ *)
let add x y = x + y

(* Polymorphic function: 'a -> 'a list -> 'a list *)
let prepend x xs = x :: xs

(* ใช้งานได้กับ type ใดก็ได้ *)
let () =
  let result1 = add 3 5 in              (* int: 8 *)
  let result2 = prepend 1 [2; 3; 4] in  (* int list: [1;2;3;4] *)
  let result3 = prepend "hi" ["there"] in (* string list *)
  Printf.printf "%d\n" result1;
  List.iter (Printf.printf "%d ") result2;
  print_newline ();
  List.iter print_endline result3

1.3.2 Predictable Garbage Collector

OCaml ใช้ generational GC ที่ออกแบบมาให้ pause time สั้นและสม่ำเสมอ ต่างจาก JVM GC ที่อาจมี stop-the-world pause ยาวหลายร้อยมิลลิวินาที

ความสัมพันธ์ระหว่าง pause time และขนาด heap แสดงได้ด้วยสมการ

Tpause = k Llive + C

โดยที่

เนื่องจาก minor heap ของ OCaml มีขนาดเล็ก (default 262,144 words หรือประมาณ 2 MB บน 64-bit) การเก็บขยะในรุ่นนี้จึงเร็วมาก (ต่ำกว่า 1 ms ในสถานการณ์ทั่วไป)

1.3.3 Zero-cost Abstractions บางส่วน

OCaml compiler มีความสามารถในการ inline และ specialize ฟังก์ชัน polymorphic ให้เป็น monomorphic code ที่ไม่มี overhead เช่น

(* Functor (module-level generic) ไม่มี runtime cost หลังจาก compile *)
module IntSet = Set.Make(Int)
module StringSet = Set.Make(String)

(* ทั้งสอง module ใช้ code เดียวกันหลัง specialization *)
let _ = IntSet.(add 1 (add 2 (add 3 empty)))
let _ = StringSet.(add "a" (add "b" empty))

1.3.4 Algebraic Effects ใน OCaml 5

OCaml 5 เป็น mainstream language แรก ที่รองรับ Algebraic Effects and Handlers ระดับภาษา ทำให้สามารถสร้าง abstraction ของ control flow ได้โดยไม่ต้องใช้ monad transformer stack ที่ซับซ้อน

1.4 Use Cases จริงในอุตสาหกรรม

%%{init: {'theme':'base', 'themeVariables': {
  'primaryColor':'#3c3836',
  'primaryTextColor':'#ebdbb2',
  'primaryBorderColor':'#928374',
  'lineColor':'#a89984',
  'clusterBkg':'#32302f',
  'clusterBorder':'#d79921'
}}}%%
flowchart LR
    subgraph Finance["การเงิน (Finance)"]
        F1["Jane Street
Trading Systems
$1T+/day"] F2["LexiFi
Derivatives DSL"] F3["Bloomberg
Risk modeling"] end subgraph Blockchain["บล็อกเชน (Blockchain)"] B1["Tezos
Smart contracts"] B2["Marigold
Layer 2"] B3["Nomadic Labs
Protocol dev"] end subgraph OS["ระบบปฏิบัติการ (OS & Infra)"] O1["MirageOS
Unikernel"] O2["Docker
Early versions"] O3["Unison
File sync"] end subgraph Tools["เครื่องมือ (Developer Tools)"] T1["Semgrep
Static analysis"] T2["Coq/Rocq
Proof assistant"] T3["Flow
JS type checker"] T4["Hack
Facebook PHP"] end OCaml[OCaml 5] OCaml --> Finance OCaml --> Blockchain OCaml --> OS OCaml --> Tools style OCaml fill:#d79921,stroke:#fabd2f,color:#282828 style Finance fill:#32302f,stroke:#98971a,color:#b8bb26 style Blockchain fill:#32302f,stroke:#458588,color:#83a598 style OS fill:#32302f,stroke:#cc241d,color:#fb4934 style Tools fill:#32302f,stroke:#b16286,color:#d3869b

1.4.1 MirageOS — Unikernel Operating System

MirageOS เป็น library operating system ที่เขียนด้วย OCaml ล้วน สร้าง unikernel ที่รันโดยตรงบน hypervisor (Xen, KVM, Firecracker) โดยไม่ต้องมี general-purpose OS คุณสมบัติเด่น

1.4.2 Tezos — Self-amending Blockchain

Tezos เป็น blockchain ที่เขียนด้วย OCaml ทั้งหมด (core protocol, node, client) เหตุผลที่เลือก OCaml

1.4.3 Jane Street — High-frequency Trading

Jane Street เป็นบริษัทการเงินที่มีนักพัฒนา OCaml มากที่สุดในโลก (หลายร้อยคน) ใช้ OCaml สำหรับ

Jane Street พัฒนา library ที่เปิดเป็น open-source เช่น Core (standard library ทดแทน), Async (concurrent programming), Base (minimal stdlib), และ Bonsai (web UI framework)

1.4.4 Semgrep — Static Analysis at Scale

Semgrep เป็นเครื่องมือ static analysis ที่สแกน code ได้หลายสิบภาษา engine เขียนด้วย OCaml เนื่องจาก

1.5 ข้อจำกัดที่ควรรู้

1.5.1 GC Pause ใน Latency-critical Systems

แม้ OCaml GC จะ predictable แต่ก็ยังมี pause time อยู่ สำหรับระบบที่ต้องการ hard real-time (เช่น medical devices, avionics) หรือ ultra-low latency (เช่น HFT sub-microsecond) อาจต้องใช้เทคนิคเพิ่มเติม

1.5.2 FFI Overhead และความเสี่ยง

แม้ FFI (Foreign Function Interface) ของ OCaml จะ overhead ต่ำ แต่การเรียก C function ยังมี cost จาก

1.5.3 Ecosystem ขนาดกลาง

เมื่อเทียบกับ Rust (crates.io มี 150,000+ packages) หรือ Go ecosystem ของ OCaml (opam มี ~4,500 packages) ยังเล็กกว่ามาก ส่งผลให้

1.5.4 Data Race ใน OCaml 5

ข้อควรระวังสำคัญ: OCaml 5 ไม่ป้องกัน data race ด้วย type system (ต่างจาก Rust ที่ใช้ Send/Sync traits) นักพัฒนาต้องออกแบบโค้ดให้

1.6 ตัวอย่าง: เปรียบเทียบ Code ระหว่าง OCaml 5 และ Rust

ตัวอย่างต่อไปนี้แสดงการเขียนโปรแกรมคำนวณผลรวมของเลข Fibonacci แบบขนาน

OCaml 5 (ใช้ Domainslib)

(* file: fib_parallel.ml *)
(* ต้องเพิ่ม domainslib ใน dune file: libraries domainslib *)

(* Sequential fibonacci - baseline *)
let rec fib n =
  if n < 2 then n
  else fib (n - 1) + fib (n - 2)

(* Parallel fibonacci using Task pool *)
let rec fib_parallel pool n =
  if n < 35 then fib n  (* threshold: สำหรับ n เล็กใช้ sequential ดีกว่า *)
  else
    let open Domainslib.Task in
    (* async spawn งานที่ 1 *)
    let a = async pool (fun () -> fib_parallel pool (n - 1)) in
    (* async spawn งานที่ 2 *)
    let b = async pool (fun () -> fib_parallel pool (n - 2)) in
    (* await ผลลัพธ์ทั้งสอง *)
    await pool a + await pool b

(* Example usage *)
let () =
  let num_domains = 4 in
  let pool = Domainslib.Task.setup_pool ~num_domains () in
  let n = 40 in
  let result =
    Domainslib.Task.run pool (fun () -> fib_parallel pool n)
  in
  Domainslib.Task.teardown_pool pool;
  Printf.printf "fib(%d) = %d\n" n result

(* รันด้วย: dune exec ./fib_parallel.exe *)
(* ผลลัพธ์: fib(40) = 102334155 *)

Rust (ใช้ Rayon)

// file: fib_parallel.rs
use rayon::join;

fn fib(n: u64) -> u64 {
    if n < 2 { n } else { fib(n - 1) + fib(n - 2) }
}

fn fib_parallel(n: u64) -> u64 {
    if n < 35 {
        fib(n)
    } else {
        // rayon::join รันสองงานแบบขนานด้วย work-stealing
        let (a, b) = join(
            || fib_parallel(n - 1),
            || fib_parallel(n - 2)
        );
        a + b
    }
}

fn main() {
    let n = 40;
    let result = fib_parallel(n);
    println!("fib({}) = {}", n, result);
}

จะเห็นว่าทั้งสองภาษามีรูปแบบคล้ายกันมาก แต่ OCaml 5 ต้องจัดการ pool เอง ขณะที่ Rust/Rayon มี global thread pool ให้ใช้โดยตรง


2. ติดตั้งและตั้งค่า Environment สำหรับ Systems Work

Installing and Configuring the Environment for Systems Work

การเตรียมสภาพแวดล้อมที่ดีคือรากฐานของการทำงานกับ OCaml อย่างมีประสิทธิภาพ ส่วนนี้จะพาทำทั้งหมดตั้งแต่ 0 จนพร้อมเขียน systems code ได้จริง

2.1 ภาพรวมของ Toolchain

%%{init: {'theme':'base', 'themeVariables': {
  'primaryColor':'#3c3836',
  'primaryTextColor':'#ebdbb2',
  'primaryBorderColor':'#928374',
  'lineColor':'#a89984',
  'clusterBkg':'#32302f',
  'clusterBorder':'#d79921'
}}}%%
flowchart TB
    subgraph Layer1["ชั้นที่ 1: Package Manager"]
        opam["opam
(OCaml Package Manager)"] end subgraph Layer2["ชั้นที่ 2: Compiler Switch"] switch["ocaml 5.2.x switch
(compiler version)"] end subgraph Layer3["ชั้นที่ 3: Build & Dev Tools"] dune["dune
(build system)"] lsp["ocaml-lsp-server
(IDE support)"] fmt["ocamlformat
(formatter)"] merlin["merlin
(editor tooling)"] end subgraph Layer4["ชั้นที่ 4: Profiling & Debug"] memtrace["memtrace
(heap profiler)"] landmarks["landmarks
(CPU profiler)"] perf["perf
(Linux profiler)"] debug["ocamldebug
(debugger)"] end subgraph Layer5["ชั้นที่ 5: Editor"] vscode["VS Code
+ OCaml Platform"] zed["Zed Editor
+ OCaml extension"] emacs["Emacs / Neovim
+ tuareg / nvim-ocaml"] end opam --> switch switch --> dune switch --> lsp switch --> fmt switch --> merlin dune --> memtrace dune --> landmarks dune --> debug lsp --> vscode lsp --> zed lsp --> emacs style opam fill:#d79921,stroke:#fabd2f,color:#282828 style switch fill:#cc241d,stroke:#fb4934,color:#ebdbb2 style Layer1 fill:#32302f,stroke:#d79921 style Layer2 fill:#32302f,stroke:#cc241d style Layer3 fill:#32302f,stroke:#98971a style Layer4 fill:#32302f,stroke:#458588 style Layer5 fill:#32302f,stroke:#b16286

2.2 ติดตั้ง opam และ OCaml 5.x

opam (OCaml Package Manager) เป็น package manager หลักของ OCaml ecosystem สิ่งที่ต้องเข้าใจคือ opam ใช้แนวคิด switch ซึ่งคล้ายกับ nvm ของ Node.js หรือ pyenv ของ Python โดยแต่ละ switch เป็น sandbox ที่มี compiler version และ packages ของตัวเอง

2.2.1 ติดตั้ง opam

บน Linux (Arch / CachyOS / EndeavourOS)

# ติดตั้ง opam จาก official repo
sudo pacman -S opam

# หรือจาก AUR ถ้าต้องการเวอร์ชันล่าสุด
yay -S opam

บน Linux (Ubuntu / Debian)

# ติดตั้งจาก apt (เวอร์ชันอาจเก่า)
sudo apt update
sudo apt install opam

# หรือใช้ bubblewrap installer สำหรับเวอร์ชันล่าสุด
bash -c "sh <(curl -fsSL https://opam.ocaml.org/install.sh)"

บน macOS

# ผ่าน Homebrew
brew install opam

บน Windows (ผ่าน WSL2 แนะนำ) หรือ native

# Native Windows (OCaml 5.2+)
winget install Git.Git OCaml.opam

2.2.2 Initialize opam

# Initialize opam (ครั้งแรกเท่านั้น)
# --bare = ไม่สร้าง default switch
opam init --bare --disable-sandboxing

# เพิ่ม opam environment เข้า shell
# สำหรับ bash/zsh
eval $(opam env --switch=default)

# เพิ่มใน ~/.bashrc หรือ ~/.zshrc ให้โหลดอัตโนมัติ
echo 'eval $(opam env)' >> ~/.bashrc

# สำหรับ fish shell
echo 'opam env | source' >> ~/.config/fish/config.fish

หมายเหตุ: --disable-sandboxing จะปิด bubblewrap sandbox ซึ่งจำเป็นถ้าใช้งานใน container หรือ WSL2 ถ้าระบบปกติควรเปิดไว้เพื่อความปลอดภัย

2.2.3 สร้าง OCaml 5 Switch

# ดูรายการ compiler ที่มีให้เลือก
opam switch list-available | grep -E "^ocaml-base-compiler.5"

# สร้าง switch ใหม่สำหรับ OCaml 5.2.0 (เวอร์ชันแนะนำ ณ ตอนนี้)
opam switch create systems-5.2 ocaml-base-compiler.5.2.0

# ใช้งาน switch
eval $(opam env --switch=systems-5.2)

# ตรวจสอบเวอร์ชัน
ocaml --version
# ผลลัพธ์: The OCaml toplevel, version 5.2.0

# ดู switch ทั้งหมดที่มี
opam switch list

2.3 ติดตั้งเครื่องมือพัฒนาหลัก

# ติดตั้ง core development tools
opam install -y \
  dune \
  ocaml-lsp-server \
  ocamlformat \
  utop \
  odoc \
  merlin

# สำหรับ systems work โดยเฉพาะ
opam install -y \
  domainslib \
  eio \
  eio_main \
  memtrace \
  memtrace_viewer \
  landmarks \
  landmarks-ppx \
  bechamel \
  core_bench \
  cmdliner \
  logs \
  fmt \
  alcotest \
  qcheck

2.4 ตั้งค่า Editor

2.4.1 VS Code Setup

  1. ติดตั้ง extension OCaml Platform (publisher: OCaml Labs)
  2. เปิด settings.json (Ctrl+Shift+P → "Preferences: Open User Settings (JSON)")
{
  "ocaml.sandbox": {
    "kind": "opam",
    "switch": "systems-5.2"
  },
  "[ocaml]": {
    "editor.defaultFormatter": "ocamllabs.ocaml-platform",
    "editor.formatOnSave": true,
    "editor.tabSize": 2
  },
  "editor.rulers": [90],
  "files.trimTrailingWhitespace": true,
  "files.insertFinalNewline": true
}

2.4.2 Zed Editor Setup

Zed รองรับ OCaml ผ่าน extension ocaml เปิดไฟล์ ~/.config/zed/settings.json

{
  "theme": "Gruvbox Dark Hard",
  "languages": {
    "OCaml": {
      "tab_size": 2,
      "formatter": {
        "external": {
          "command": "ocamlformat",
          "arguments": ["--name", "{buffer_path}", "-"]
        }
      },
      "format_on_save": "on",
      "language_servers": ["ocaml-lsp-server"]
    }
  },
  "lsp": {
    "ocaml-lsp-server": {
      "binary": {
        "path_lookup": true
      }
    }
  }
}

2.4.3 Neovim Setup (พิเศษสำหรับ Hyprland users)

-- ~/.config/nvim/lua/plugins/ocaml.lua
return {
  {
    "neovim/nvim-lspconfig",
    opts = {
      servers = {
        ocamllsp = {
          cmd = { "ocamllsp" },
          filetypes = { "ocaml", "ocaml.menhir", "ocaml.interface", "ocaml.ocamllex", "reason", "dune" },
          root_dir = function(fname)
            return require("lspconfig.util").root_pattern(
              "*.opam", "esy.json", "package.json", ".merlin", "dune-project", "dune-workspace"
            )(fname)
          end,
        },
      },
    },
  },
}

2.5 สร้าง .ocamlformat Config

ไฟล์ .ocamlformat วางที่ root ของโปรเจคเพื่อตั้งค่า formatter

# .ocamlformat
version = 0.26.2
profile = default
margin = 90
parse-docstrings = true
break-infix = fit-or-vertical
wrap-comments = true

2.6 สร้าง Project แรกด้วย dune

2.6.1 สร้างโครงสร้างโปรเจค

# สร้างโฟลเดอร์และ initialize project
cd ~/projects
dune init proj moo_systems --kind=executable

# เข้าไปในโปรเจค
cd moo_systems

# ดูโครงสร้าง
tree .

ผลลัพธ์จะได้โครงสร้างประมาณนี้

moo_systems/
├── bin/
│   ├── dune           # Build config สำหรับ executable
│   └── main.ml        # Entry point
├── lib/
│   └── dune           # Build config สำหรับ library
├── test/
│   ├── dune           # Build config สำหรับ tests
│   └── test_moo_systems.ml
├── dune-project       # Project-level config
└── moo_systems.opam   # opam package metadata (generated)

2.6.2 แก้ไข dune-project

(lang dune 3.15)

(name moo_systems)

(generate_opam_files true)

(source (github moo/moo_systems))

(authors "Moo <moo@rmutsv.ac.th>")

(maintainers "Moo <moo@rmutsv.ac.th>")

(license MIT)

(package
 (name moo_systems)
 (synopsis "OCaml 5 systems programming playground")
 (description "Learning OCaml 5 for systems development")
 (depends
  (ocaml (>= 5.2.0))
  dune
  (domainslib (>= 0.5.1))
  (eio (>= 1.0))
  (eio_main (>= 1.0))
  (cmdliner (>= 1.2.0))
  logs
  fmt
  (alcotest :with-test)))

2.6.3 แก้ไข bin/dune

(executable
 (name main)
 (public_name moo_systems)
 (libraries
  moo_systems
  domainslib
  eio
  eio_main
  cmdliner
  logs
  fmt))

2.6.4 เขียนโปรแกรมแรก

(* file: bin/main.ml *)
(* โปรแกรมตัวอย่างแสดง:
   1. Basic OCaml 5 features
   2. Domains parallelism
   3. Effect handlers
*)

(* ---------- Part A: Basic features ---------- *)

(* Algebraic data type สำหรับเก็บข้อมูล system info *)
type os_kind =
  | Linux of string  (* distro name *)
  | Macos
  | Windows
  | Unknown

type system_info = {
  os: os_kind;
  cpu_count: int;
  hostname: string;
}

(* Pattern matching แบบ exhaustive *)
let os_to_string = function
  | Linux distro -> Printf.sprintf "Linux (%s)" distro
  | Macos -> "macOS"
  | Windows -> "Windows"
  | Unknown -> "Unknown OS"

(* อ่าน system info *)
let get_system_info () : system_info =
  let hostname = Unix.gethostname () in
  let cpu_count = Domain.recommended_domain_count () in
  let os =
    (* ตรวจสอบ OS อย่างง่าย ๆ ผ่าน environment *)
    match Sys.os_type with
    | "Unix" ->
      if Sys.file_exists "/etc/os-release" then Linux "detected"
      else Macos  (* สมมติ macOS ถ้าไม่มี /etc/os-release *)
    | "Win32" | "Cygwin" -> Windows
    | _ -> Unknown
  in
  { os; cpu_count; hostname }

(* ---------- Part B: Parallelism with Domains ---------- *)

(* ฟังก์ชันที่ใช้ CPU หนัก *)
let heavy_computation n =
  let rec loop i acc =
    if i = 0 then acc
    else loop (i - 1) (acc +. sin (float_of_int i) *. cos (float_of_int i))
  in
  loop n 0.0

(* รัน 4 งานขนานกันด้วย Domain *)
let parallel_demo () =
  Printf.printf "\n--- Parallel computation demo ---\n";
  let iterations = 10_000_000 in

  (* Spawn 4 domains พร้อมกัน *)
  let d1 = Domain.spawn (fun () -> heavy_computation iterations) in
  let d2 = Domain.spawn (fun () -> heavy_computation iterations) in
  let d3 = Domain.spawn (fun () -> heavy_computation iterations) in
  let d4 = Domain.spawn (fun () -> heavy_computation iterations) in

  (* รอให้ทุก domain ทำงานเสร็จ และรวมผลลัพธ์ *)
  let results =
    [ Domain.join d1; Domain.join d2
    ; Domain.join d3; Domain.join d4 ]
  in
  let sum = List.fold_left (+.) 0.0 results in
  Printf.printf "Sum from 4 parallel domains: %f\n" sum

(* ---------- Part C: Effect Handler (OCaml 5 flagship feature) ---------- *)

(* ประกาศ effect ใหม่: ถามค่าจาก environment *)
type _ Effect.t += Ask : string -> string Effect.t

(* ฟังก์ชันที่ perform effect *)
let greet_user () =
  let name = Effect.perform (Ask "name") in
  let role = Effect.perform (Ask "role") in
  Printf.sprintf "Hello %s, the %s!" name role

(* Handler: interpret effect *)
let run_with_static_answers computation =
  let open Effect.Deep in
  try_with computation ()
    { effc = fun (type a) (eff : a Effect.t) ->
        match eff with
        | Ask "name" ->
          Some (fun (k : (a, _) continuation) -> continue k "Moo")
        | Ask "role" ->
          Some (fun (k : (a, _) continuation) -> continue k "Lecturer")
        | _ -> None
    }

let effect_demo () =
  Printf.printf "\n--- Effect handler demo ---\n";
  let greeting = run_with_static_answers greet_user in
  Printf.printf "%s\n" greeting

(* ---------- Main ---------- *)

let () =
  let info = get_system_info () in
  Printf.printf "=== Moo's OCaml 5 Systems Demo ===\n";
  Printf.printf "Hostname: %s\n" info.hostname;
  Printf.printf "OS: %s\n" (os_to_string info.os);
  Printf.printf "Recommended domains: %d\n" info.cpu_count;

  parallel_demo ();
  effect_demo ();

  Printf.printf "\nDone!\n"

2.6.5 Build และ Run

# Build โปรเจค
dune build

# Run
dune exec moo_systems

# Build + run ในคำสั่งเดียว (useful สำหรับ dev loop)
dune exec ./bin/main.exe

# Watch mode: rebuild อัตโนมัติเมื่อไฟล์เปลี่ยน
dune build --watch

# Clean build artifacts
dune clean

ตัวอย่างผลลัพธ์

=== Moo's OCaml 5 Systems Demo ===
Hostname: moo-cachyos
OS: Linux (detected)
Recommended domains: 16

--- Parallel computation demo ---
Sum from 4 parallel domains: -2.847502

--- Effect handler demo ---
Hello Moo, the Lecturer!

Done!

2.7 ติดตั้ง Profiling Tools

2.7.1 perf (Linux)

# บน CachyOS / Arch
sudo pacman -S perf

# บน Ubuntu / Debian
sudo apt install linux-tools-common linux-tools-generic

# ตรวจสอบ
perf --version

การใช้งานพื้นฐาน

# Compile โปรแกรมพร้อม debug symbols
OCAMLFIND_FLAGS="-g" dune build --profile=release

# Profile โปรแกรม
perf record -g ./_build/default/bin/main.exe
perf report  # เปิด interactive TUI

# Flamegraph
perf record -g -F 99 ./_build/default/bin/main.exe
perf script > out.perf
# ใช้ FlameGraph tool จาก https://github.com/brendangregg/FlameGraph

2.7.2 landmarks (OCaml-specific CPU profiler)

(* เพิ่ม ppx_landmarks ใน dune file:
   (preprocess (pps landmarks-ppx --auto))
*)

(* แค่ build ด้วย flag ก็ profile ทุก function อัตโนมัติ *)
let compute x =
  let y = x * 2 in
  Thread.delay 0.1;
  y + 1

let () =
  let _ = compute 42 in
  ()
# รันโดยเปิด landmarks
OCAML_LANDMARKS=on dune exec ./bin/main.exe

# Export เป็น JSON เพื่อวิเคราะห์
OCAML_LANDMARKS="on,output=profile.json,format=json" dune exec ./bin/main.exe

2.7.3 memtrace (Heap profiler)

(* เพิ่มบรรทัดนี้ตอนเริ่มโปรแกรม *)
let () =
  Memtrace.trace_if_requested ();
  (* ... main code ... *)
  ()
# รันพร้อม tracing
MEMTRACE=/tmp/trace.ctf dune exec ./bin/main.exe

# View ด้วย memtrace_viewer
memtrace-viewer /tmp/trace.ctf
# เปิด browser ที่ http://localhost:8080

2.8 ตัวอย่าง Makefile สำหรับโปรเจค Systems

เพื่อให้ workflow เร็วขึ้น สร้างไฟล์ Makefile ที่ root ของโปรเจค

# Makefile for moo_systems
.PHONY: all build release test fmt lint clean watch repl profile bench

# Default target
all: build

# Development build
build:
	dune build

# Optimized release build
release:
	dune build --profile=release

# Run tests
test:
	dune runtest --force

# Format all source files
fmt:
	dune build @fmt --auto-promote

# Lint (type-check only, no codegen)
lint:
	dune build @check

# Clean all artifacts
clean:
	dune clean

# Watch mode for dev
watch:
	dune build --watch

# Interactive REPL with project loaded
repl:
	dune utop lib

# Run with landmarks profiling
profile:
	OCAML_LANDMARKS="on,output=profile.txt" dune exec ./bin/main.exe
	@echo "Profile saved to profile.txt"

# Run with memtrace
memtrace:
	MEMTRACE=/tmp/moo-trace.ctf dune exec ./bin/main.exe
	@echo "Memory trace saved to /tmp/moo-trace.ctf"
	@echo "View with: memtrace-viewer /tmp/moo-trace.ctf"

# Run benchmarks
bench:
	dune exec ./bench/bench.exe

# Install as opam package (local)
install:
	dune install

# Setup dev environment (first time)
setup:
	opam install . --deps-only --with-test -y
	@echo "Environment ready. Run: eval \$$(opam env)"

2.9 Checklist สำหรับ Systems-ready OCaml Environment

# รายการตรวจสอบ คำสั่งตรวจสอบ ผลลัพธ์ที่คาดหวัง
1 opam ติดตั้งแล้ว opam --version 2.2.0 หรือใหม่กว่า
2 OCaml 5.x active ocaml --version 5.2.0 หรือใหม่กว่า
3 dune พร้อมใช้ dune --version 3.15.x หรือใหม่กว่า
4 LSP ทำงาน which ocamllsp ~/.opam/systems-5.2/bin/ocamllsp
5 Formatter พร้อม ocamlformat --version 0.26.x หรือใหม่กว่า
6 Domainslib ติดตั้งแล้ว opam list domainslib มีในรายการ
7 Eio ติดตั้งแล้ว opam list eio มีในรายการ
8 Multicore ใช้งานได้ ocaml -e "print_int (Domain.recommended_domain_count ())" เลขจำนวน CPU cores
9 memtrace พร้อม which memtrace-viewer มี path
10 utop ทำงาน utop เข้าสู่ REPL

2.10 เทคนิคการใช้ utop สำหรับทดลอง

utop คือ REPL ที่ใช้งานง่ายกว่า ocaml native และมี syntax highlighting, auto-complete

# เริ่ม utop
utop

# โหลด library
#require "domainslib";;
#require "eio_main";;

# ลอง code
let n = Domain.recommended_domain_count ();;
(* val n : int = 16 *)

# Load project library
#require "moo_systems";;

# Open module
open Domainslib;;

# ออก
#quit;;

เทคนิคที่เป็นประโยชน์

(* ดู type ของ expression *)
let x = 42;;
(* val x : int = 42 *)

(* โหลดไฟล์ทั้งไฟล์ *)
#use "my_module.ml";;

(* ดู directives ทั้งหมด *)
#help;;

(* เปิด timing ของแต่ละ command *)
#time;;
let _ = List.init 1_000_000 (fun i -> i * i);;
(* แสดง time ที่ใช้ *)

สรุปส่วนที่ 1

ในส่วนที่ 1 นี้ เราได้เห็นภาพรวมของ OCaml 5 ในบริบทของ systems programming ทั้งในแง่

  1. ความสามารถของภาษา ที่ผสม type safety, performance และ concurrency ได้อย่างลงตัว
  2. ตำแหน่งในตลาด เมื่อเทียบกับ Rust, Go, C++ พร้อม use cases จริงจาก Jane Street, Tezos, MirageOS, Semgrep
  3. ข้อจำกัด ที่ควรรู้เพื่อตัดสินใจเลือกเครื่องมือให้เหมาะกับงาน
  4. การติดตั้งและตั้งค่า environment แบบครบวงจร ตั้งแต่ opam จนถึง profiling tools

ในส่วนต่อไป เราจะเจาะลึก type system และ memory layout ของ OCaml ซึ่งเป็นพื้นฐานที่จำเป็นก่อนเริ่มเขียน systems code ที่มีประสิทธิภาพ