
ภาคผนวกนี้รวบรวม cheatsheets, ตารางเปรียบเทียบเชิงลึก (in-depth comparison tables), แผนที่ระบบนิเวศ (ecosystem map), และทรัพยากรอ้างอิง (reference resources) ที่จำเป็นสำหรับนักพัฒนา OCaml 5 สายงาน systems programming โดยเนื้อหาทุกส่วนมาพร้อมตัวอย่างโค้ดที่ทดลองใช้จริงได้ (runnable examples) และการเปรียบเทียบเชิงเทคนิคแบบตรงประเด็นกับ Rust และ Go
เปรียบเทียบ concurrency primitives ทั้งสามภาษาในระดับ mapping ทีละ primitive พร้อมโค้ดตัวอย่างที่ทำงานได้จริง เพื่อให้นักพัฒนาที่ย้ายระหว่างภาษาสามารถหาคู่เทียบ (counterpart) ของ primitive ที่คุ้นเคยได้อย่างรวดเร็ว
OCaml 5, Rust, และ Go เลือกใช้โมเดล concurrency ที่ต่างกันในระดับพื้นฐาน (fundamentally different models) ทำให้ primitive ที่ใช้ก็ต่างกันตามไปด้วย
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#3c3836','primaryTextColor':'#ebdbb2','primaryBorderColor':'#83a598','lineColor':'#a89984','secondaryColor':'#504945','tertiaryColor':'#282828','background':'#282828','mainBkg':'#3c3836','nodeBorder':'#83a598','clusterBkg':'#32302f','clusterBorder':'#fabd2f','titleColor':'#fabd2f','edgeLabelBackground':'#282828'}}}%%
flowchart LR
subgraph OCaml["OCaml 5 Model"]
D1[Domain 1
OS Thread + Minor Heap]
D2[Domain 2
OS Thread + Minor Heap]
F1[Fiber A
Effect-based]
F2[Fiber B
Effect-based]
D1 --> F1
D1 --> F2
end
subgraph Rust["Rust Model"]
T1[OS Thread 1]
T2[OS Thread 2]
A1[async Task A]
A2[async Task B]
T1 --> A1
T1 --> A2
end
subgraph Go["Go Model"]
M1[M:N Scheduler
GOMAXPROCS]
G1[goroutine 1]
G2[goroutine 2]
G3[goroutine N]
M1 --> G1
M1 --> G2
M1 --> G3
end
ตารางด้านล่างจับคู่ primitive ที่ให้ความหมายใกล้เคียงกัน (semantic equivalents) ระหว่างสามภาษา เพื่อให้มองเห็นได้ในครั้งเดียวว่าแต่ละโมเดลเลือกใช้ตัวไหนสำหรับงานเดียวกัน
| วัตถุประสงค์ (Purpose) | OCaml 5 | Rust | Go |
|---|---|---|---|
| Spawn parallel work (OS thread) | Domain.spawn |
std::thread::spawn |
go keyword (auto-managed) |
| Lightweight task / green thread | Eio.Fiber.fork |
tokio::spawn |
go keyword |
| Join / wait completion | Domain.join |
JoinHandle::join |
sync.WaitGroup |
| Mutual exclusion | Mutex.create |
std::sync::Mutex |
sync.Mutex |
| Read-write lock | Rwlock (stdlib ใน 5.1+) |
std::sync::RwLock |
sync.RWMutex |
| Atomic variable | Atomic.make |
std::sync::atomic::AtomicXxx |
sync/atomic package |
| Compare-and-swap (CAS) | Atomic.compare_and_set |
AtomicXxx::compare_exchange |
atomic.CompareAndSwapXxx |
| Condition variable | Condition.create |
std::sync::Condvar |
sync.Cond |
| Semaphore | Semaphore.Counting.make |
tokio::sync::Semaphore |
chan ที่ buffered |
| Channel / message passing | Eio.Stream.create หรือ Domainslib.Chan |
std::sync::mpsc / tokio::sync::mpsc |
chan T |
| Unbounded channel | Eio.Stream.create max_int |
crossbeam::channel::unbounded |
make(chan T) (unbuffered) |
| Bounded channel | Eio.Stream.create n |
tokio::sync::mpsc::channel(n) |
make(chan T, n) |
| Select on multiple channels | Eio.Fiber.any |
tokio::select! |
select { case ... } |
| Cancellation | Eio.Switch / Fiber.cancel |
CancellationToken / drop future |
context.Context |
| Structured concurrency | Eio.Switch.run |
tokio::task::JoinSet |
errgroup.Group (third-party) |
| Parallel for loop | Domainslib.Task.parallel_for |
rayon::par_iter |
manual + sync.WaitGroup |
| Task pool / work stealing | Domainslib.Task.setup_pool |
rayon::ThreadPoolBuilder |
built-in scheduler |
| Cooperative yield | Eio.Fiber.yield |
tokio::task::yield_now |
runtime.Gosched |
| Sleep / timer | Eio.Time.sleep |
tokio::time::sleep |
time.Sleep |
| Data race prevention | ไม่มี compile-time (runtime only) | ✅ compile-time (Send + Sync) | runtime detector (-race) |
ต่อไปนี้คือตัวอย่าง "spawn ทำงานคู่ขนานแล้ว join" — งานพื้นฐานที่สุดของ concurrency — เขียนในทั้งสามภาษาเพื่อให้เห็นความแตกต่างของ syntax และ semantics
OCaml 5 — Domains (true parallelism)
(* file: spawn_join.ml *)
(* คอมไพล์: ocamlfind ocamlopt -package domainslib -linkpkg spawn_join.ml *)
(* หรือใช้ dune: dune exec ./spawn_join.exe *)
(* ฟังก์ชันหนักที่จะให้แต่ละ Domain ทำ — จำลอง CPU-bound work *)
let heavy_work label n =
let sum = ref 0 in
for i = 1 to n do
sum := !sum + i
done;
Printf.printf "[%s] sum = %d\n%!" label !sum;
!sum
let () =
(* Domain.spawn สร้าง OS thread ใหม่ + minor heap แยก = parallelism จริง *)
let d1 = Domain.spawn (fun () -> heavy_work "domain-1" 10_000_000) in
let d2 = Domain.spawn (fun () -> heavy_work "domain-2" 20_000_000) in
(* Domain.join รอให้ทำเสร็จและคืนค่าผลลัพธ์ *)
let r1 = Domain.join d1 in
let r2 = Domain.join d2 in
Printf.printf "total = %d\n" (r1 + r2)
(* ผลลัพธ์ที่คาดหวัง (ลำดับอาจสลับได้):
[domain-1] sum = 50000005000000
[domain-2] sum = 200000010000000
total = 250000015000000 *)
Rust — std::thread
// file: spawn_join.rs
// คอมไพล์: rustc spawn_join.rs -O
use std::thread;
fn heavy_work(label: &str, n: u64) -> u64 {
let sum: u64 = (1..=n).sum();
println!("[{}] sum = {}", label, sum);
sum
}
fn main() {
// thread::spawn คืน JoinHandle — ต้อง move ค่าที่ใช้ข้าม thread เพราะ ownership
let h1 = thread::spawn(|| heavy_work("thread-1", 10_000_000));
let h2 = thread::spawn(|| heavy_work("thread-2", 20_000_000));
// join คืน Result<T, Box<dyn Any>> — ต้อง unwrap panic case
let r1 = h1.join().unwrap();
let r2 = h2.join().unwrap();
println!("total = {}", r1 + r2);
}
Go — goroutines + WaitGroup
// file: spawn_join.go
// run: go run spawn_join.go
package main
import (
"fmt"
"sync"
)
func heavyWork(label string, n uint64, out *uint64, wg *sync.WaitGroup) {
defer wg.Done() // แจ้ง WaitGroup เมื่อทำเสร็จ
var sum uint64 = 0
for i := uint64(1); i <= n; i++ {
sum += i
}
fmt.Printf("[%s] sum = %d\n", label, sum)
*out = sum
}
func main() {
var wg sync.WaitGroup
var r1, r2 uint64
wg.Add(2)
go heavyWork("goroutine-1", 10_000_000, &r1, &wg)
go heavyWork("goroutine-2", 20_000_000, &r2, &wg)
wg.Wait()
fmt.Printf("total = %d\n", r1+r2)
}
ข้อสังเกตสำคัญ (Key Observations):
Domain) vs concurrency (Fiber) — เขียนต่างกันSend — ถ้าลืมจะ error ตั้งแต่คอมไพล์ (compile-time data race prevention)WaitGroup) และไม่มี return value จาก go โดยตรงOCaml 5 — Eio.Stream
(* file: channel_demo.ml
dune: (executables (names channel_demo) (libraries eio_main)) *)
open Eio.Std
let producer stream =
for i = 1 to 5 do
Eio.Stream.add stream i;
traceln "produced: %d" i
done;
Eio.Stream.add stream (-1) (* sentinel — สัญญาณว่าจบแล้ว *)
let consumer stream =
let rec loop () =
match Eio.Stream.take stream with
| -1 -> traceln "consumer: done"
| n -> traceln "consumed: %d" n; loop ()
in
loop ()
let () =
Eio_main.run @@ fun _env ->
let stream = Eio.Stream.create 3 in (* bounded buffer = 3 *)
Fiber.both
(fun () -> producer stream)
(fun () -> consumer stream)
Go — channel + range
// file: channel_demo.go
package main
import "fmt"
func main() {
ch := make(chan int, 3) // bounded channel size 3
go func() {
for i := 1; i <= 5; i++ {
ch <- i
fmt.Printf("produced: %d\n", i)
}
close(ch) // ปิด channel เพื่อ signal consumer
}()
for n := range ch { // range block จนกว่า channel ปิด
fmt.Printf("consumed: %d\n", n)
}
}
Rust — tokio::mpsc
// file: channel_demo.rs — ต้องมี [dependencies] tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() {
let (tx, mut rx) = tokio::sync::mpsc::channel::<i32>(3);
tokio::spawn(async move {
for i in 1..=5 {
tx.send(i).await.unwrap();
println!("produced: {}", i);
}
// drop(tx) อัตโนมัติเมื่อ task จบ → rx.recv() จะคืน None
});
while let Some(n) = rx.recv().await {
println!("consumed: {}", n);
}
}
สามภาษา สามปรัชญาในการรับประกัน memory safety: OCaml ใช้ generational GC ที่ปรับแต่งได้ (tunable), Rust ใช้ ownership + borrow checker ที่พิสูจน์ได้ตอนคอมไพล์ (compile-time), และ Go ใช้ concurrent GC ที่ออกแบบมาเพื่อ low-latency บน server workload
การรับประกันความปลอดภัยของหน่วยความจำ (memory safety) สามารถทำได้หลายวิธี แต่ละวิธีแลกเปลี่ยน (trade off) อย่างต่างกันระหว่าง developer ergonomics, runtime performance, และ predictability
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#3c3836','primaryTextColor':'#ebdbb2','primaryBorderColor':'#83a598','lineColor':'#a89984','secondaryColor':'#504945','background':'#282828','mainBkg':'#3c3836','nodeBorder':'#b8bb26','clusterBkg':'#32302f','clusterBorder':'#fabd2f','edgeLabelBackground':'#282828'}}}%%
flowchart TB
Start([Memory Safety Requirement]) --> Q1{ต้องการ
zero-overhead?}
Q1 -->|Yes| Rust[Rust
Ownership + Borrow]
Q1 -->|No| Q2{Pause time
สำคัญ?}
Q2 -->|Very| Go[Go
Concurrent GC
<1ms pause]
Q2 -->|Moderate| OCaml[OCaml
Generational GC
tunable]
Rust --> R1[+ No runtime overhead
+ Predictable
- Steep learning curve]
Go --> G1[+ Easy to write
+ Low pause
- Throughput cost]
OCaml --> O1[+ Immutable by default
+ Short minor GC
- Tuning needed]
| คุณสมบัติ (Feature) | OCaml 5 | Rust | Go |
|---|---|---|---|
| กลไกหลัก (Primary mechanism) | Generational GC | Ownership + Borrow checker | Concurrent mark-sweep GC |
| Check time | Runtime | Compile-time | Runtime |
| Null safety | ✅ (via option type) |
✅ (via Option<T>) |
❌ (nil pointer panic runtime) |
| Use-after-free prevention | ✅ (GC) | ✅ (lifetimes) | ✅ (GC) |
| Double-free prevention | ✅ | ✅ (single owner) | ✅ |
| Data race prevention | ❌ (runtime concern) | ✅ (Send + Sync traits) | ❌ (need -race detector) |
| Memory leak prevention | ❌ (cycle collector ช่วย) | ❌ (Rc cycle leaks) | ❌ (goroutine leaks common) |
| Typical pause time | ~1–10 ms (minor), variable (major) | 0 ms (no GC) | <1 ms (sub-ms target) |
| Memory overhead vs C | ~1.5–3× | ~1.0× (สมจริง) | ~2–3× |
| Throughput overhead | 5–15% GC work | 0% | 10–20% GC work |
| Manual tuning needed | ปานกลาง (Gc.set) | ไม่ต้อง | น้อยมาก (GOGC) |
| Off-heap memory support | ✅ (Bigarray) | ✅ (Box, Vec, raw pointers) | ⚠️ (unsafe package) |
| FFI safety | ⚠️ manual pinning | ✅ with care | ⚠️ cgo overhead |
OCaml GC สร้างอยู่บน generational hypothesis ที่ว่า object ส่วนใหญ่ตายเร็ว (most objects die young) — การสังเกตเชิงประจักษ์ที่พบได้ในเกือบทุก workload ของ functional programming
ถ้าให้ $p(t)$ คือความน่าจะเป็นที่ object จะยังมีชีวิตอยู่หลังเวลา $t$ (survival probability) และพบว่า distribution นี้ decay แบบ exponential ในช่วงต้น คณิตศาสตร์ของ minor GC cost จะเป็นดังนี้:
โดยที่:
เนื่องจาก $L \ll N$ (จำนวน allocation ทั้งหมดใน minor heap) ตาม generational hypothesis ทำให้ minor GC cost เป็นสัดส่วนกับ live data ไม่ใช่ allocated data — นี่คือเหตุผลที่ minor GC ใน OCaml เร็วมากแม้ allocate หนัก
สำหรับ throughput utilization ของโปรแกรม:
โดยที่:
ค่า $U$ สำหรับ OCaml โดยทั่วไปอยู่ที่ 0.85–0.95 (GC overhead 5–15%) ขึ้นอยู่กับ space_overhead ที่ตั้ง — เทียบกับ Rust ที่ $U = 1.0$ (ไม่มี GC)
(* file: gc_tuning.ml *)
(* ตัวอย่างการปรับจูน GC สำหรับ workload ต่างกัน *)
let print_gc_stats label =
let s = Gc.stat () in
Printf.printf "\n=== %s ===\n" label;
Printf.printf " minor collections : %d\n" s.minor_collections;
Printf.printf " major collections : %d\n" s.major_collections;
Printf.printf " heap words : %d (%.2f MB)\n"
s.heap_words (float_of_int s.heap_words *. 8.0 /. 1024.0 /. 1024.0);
Printf.printf " live words : %d\n" s.live_words;
Printf.printf " promoted words : %d\n" s.promoted_words
(* Workload 1: allocation-heavy — ทำซ้ำสร้าง list สั้นๆ *)
let allocation_heavy () =
for _ = 1 to 100_000 do
let _ = List.init 100 (fun i -> i * 2) in
()
done
let () =
(* ค่า default *)
print_gc_stats "Before tuning (default)";
allocation_heavy ();
print_gc_stats "After default run";
(* ปรับให้ minor heap ใหญ่ขึ้น = major GC น้อยลง แต่ใช้ RAM มากขึ้น *)
Gc.set { (Gc.get ()) with
minor_heap_size = 1 lsl 20; (* 1M words = 8MB *)
space_overhead = 120; (* default 80; ใหญ่ขึ้น = GC เบาลง *)
};
Gc.compact (); (* reset stats เพื่อเปรียบเทียบ *)
allocation_heavy ();
print_gc_stats "After tuned run"
(* การรัน:
ocaml gc_tuning.ml
ผลที่คาดหวัง: After tuned run จะมี major_collections น้อยกว่ามาก
แต่ heap_words ใหญ่กว่า (trade-off: throughput vs memory) *)
OCaml 5 — GC จัดการให้ ไม่ต้องเขียน lifetime
let get_ref () =
let x = ref 42 in
x (* คืน ref กลับ — GC จะรักษาไว้ตราบใดที่มี reachable path *)
let () =
let r = get_ref () in
Printf.printf "%d\n" !r (* ปลอดภัย — x ยังมีชีวิตอยู่ *)
Rust — compiler บังคับ lifetime
// โค้ดนี้ "ไม่ผ่าน" borrow checker
fn get_ref() -> &'static i32 {
let x = 42;
&x // ❌ error: `x` does not live long enough
}
// วิธีที่ถูกคือ move ownership หรือใช้ Box
fn get_ref_ok() -> Box<i32> {
Box::new(42) // heap allocation, ownership transferred
}
Go — GC จัดการ เหมือน OCaml
func getRef() *int {
x := 42
return &x // escape analysis จะ promote x ไป heap อัตโนมัติ
}
ข้อสรุป: OCaml และ Go ให้ developer ergonomics ที่คล้ายกัน (ไม่ต้องคิดเรื่อง lifetime) แลกกับ GC overhead ส่วน Rust บังคับให้ developer จัดการเอง แลกกับ zero overhead และความปลอดภัยที่พิสูจน์ได้
แผนที่ระบบนิเวศของ OCaml ในงาน systems programming จัดกลุ่มตามโดเมน (domain) พร้อมคำแนะนำการเลือกใช้ (selection guide) ระบุ package ที่เป็น production-grade และสถานะการบำรุงรักษา (maintenance status) ในปัจจุบัน
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#3c3836','primaryTextColor':'#ebdbb2','primaryBorderColor':'#83a598','lineColor':'#a89984','secondaryColor':'#504945','background':'#282828','mainBkg':'#3c3836','nodeBorder':'#8ec07c','clusterBkg':'#32302f','clusterBorder':'#fe8019','edgeLabelBackground':'#282828','titleColor':'#fabd2f'}}}%%
flowchart TB
Core((OCaml 5
Systems Core))
subgraph Build["Build & Tools"]
dune
opam
ocamlformat
ocaml-lsp-server
odoc
end
subgraph Concur["Concurrency"]
eio
domainslib
lwt
async
end
subgraph Net["Networking"]
cohttp
dream
conduit
tls
mirage-crypto
end
subgraph Data["Data & Serialization"]
angstrom
faraday
yojson
cstruct
zarith
end
subgraph DB["Database"]
caqti
irmin
index
end
subgraph CLI["CLI & Logging"]
cmdliner
logs
fmt
bos
end
subgraph Test["Testing"]
alcotest
qcheck
bechamel
crowbar
end
Core --> Build
Core --> Concur
Core --> Net
Core --> Data
Core --> DB
Core --> CLI
Core --> Test
เครื่องมือพื้นฐานที่ทุกโปรเจกต์ควรมี (foundation tooling) ไม่ว่าจะเขียน systems code ประเภทใด
| Package | หน้าที่ (Role) | สถานะ (Status) | คำสั่งติดตั้ง (Install) |
|---|---|---|---|
dune |
Build system หลัก (de facto standard) | ✅ Active | opam install dune |
opam |
Package manager | ✅ Active | ติดตั้งระดับระบบ |
ocamlformat |
Code formatter | ✅ Active | opam install ocamlformat |
ocaml-lsp-server |
LSP server สำหรับ editor | ✅ Active | opam install ocaml-lsp-server |
merlin |
Semantic analysis (ใช้ร่วมกับ LSP) | ✅ Active | opam install merlin |
odoc |
Documentation generator | ✅ Active | opam install odoc |
utop |
Enhanced REPL | ✅ Active | opam install utop |
dune-release |
Release automation | ✅ Active | opam install dune-release |
Eio เป็นตัวเลือกใหม่ที่แนะนำสำหรับโปรเจกต์ OCaml 5 ใหม่ๆ เนื่องจากใช้ประโยชน์จาก effect handlers และ direct-style โค้ด (no monadic >>=)
| Package | Paradigm | เมื่อไหร่ใช้ (When to use) |
|---|---|---|
eio + eio_main |
Effect-based, direct style | โปรเจกต์ใหม่ใน OCaml 5 — แนะนำเป็น default |
domainslib |
Task pool + parallel_for | CPU-bound parallelism (numerical, data processing) |
lwt + lwt_ppx |
Promise/monadic style | Legacy codebases, หรือ ecosystem lock-in |
async (Jane Street) |
Promise/monadic style | ใช้ร่วมกับ Core/Base ecosystem |
saturn |
Lock-free data structures | เมื่อต้องการ concurrent data structure ที่เร็วกว่า Mutex |
kcas |
Software transactional memory | Multi-word atomic updates |
| Package | หน้าที่ | หมายเหตุ |
|---|---|---|
cohttp-eio |
HTTP client/server (low-level) | ใช้ร่วมกับ Eio; production-grade |
dream |
Web framework (high-level) | คล้าย Flask/Sinatra; built on Httpaf |
httpaf |
HTTP/1 low-level parser | Zero-copy, เร็วมาก |
h2 |
HTTP/2 implementation | Pure OCaml |
tls / ocaml-tls |
TLS pure OCaml | ใช้ใน MirageOS, audit-friendly |
mirage-crypto |
Crypto primitives | Modern algorithms, AEAD support |
conduit |
Connection abstraction | รวม TCP, Unix sockets, TLS ในอินเทอร์เฟซเดียว |
uri |
URI parsing RFC 3986 | Standard library-grade |
ocaml-protoc |
Protocol Buffers codegen | สำหรับ gRPC ecosystem |
grpc-eio |
gRPC client/server | Eio-based |
| Package | รูปแบบ (Format) | จุดเด่น |
|---|---|---|
angstrom |
Binary/text parser combinator | Fast, resumable parsing (streaming) |
faraday |
Serialization | คู่กับ Angstrom สำหรับ write |
yojson |
JSON | Standard; ppx_yojson_conv สำหรับ auto-derive |
jsonaf |
JSON (Jane Street) | เร็วกว่า Yojson; Core ecosystem |
cstruct |
Binary buffer (typed) | คล้าย Rust zerocopy |
bigarray |
C-compatible arrays | Built-in; off-heap memory |
zarith |
Arbitrary-precision integer | ใช้ GMP; สำคัญสำหรับ crypto/finance |
hex |
Hex encoding/decoding | Trivial แต่ใช้บ่อย |
base64 |
Base64 | RFC-compliant |
sexplib + ppx_sexp_conv |
S-expressions | Jane Street standard serialization |
| Package | ประเภท | หมายเหตุ |
|---|---|---|
caqti |
Unified DB interface | รองรับ PostgreSQL, SQLite, MariaDB; async via Lwt/Eio |
caqti-eio |
Eio driver | สำหรับ OCaml 5 projects |
caqti-lwt |
Lwt driver | สำหรับ legacy codebase |
postgresql-ocaml |
PostgreSQL binding | Direct binding; lower-level |
irmin |
Git-like distributed store | Pure OCaml, MirageOS origin |
index |
On-disk key-value | Backing store สำหรับ Irmin |
sqlite3 |
SQLite direct binding | Minimal wrapper |
(* ตัวอย่าง: CLI + logging + config — pattern มาตรฐานสำหรับ systems tool *)
(* dune: (executables (names mytool)
(libraries cmdliner logs logs.cli logs.fmt fmt.tty)) *)
open Cmdliner
(* กำหนด logger สำหรับแต่ละ module *)
let src = Logs.Src.create "mytool" ~doc:"My systems tool"
module Log = (val Logs.src_log src : Logs.LOG)
(* subcommand: serve *)
let serve_cmd port host =
Log.info (fun m -> m "starting server on %s:%d" host port);
(* ... server code ... *)
`Ok 0
let port =
let doc = "Port to listen on" in
Arg.(value & opt int 8080 & info ["p"; "port"] ~doc ~docv:"PORT")
let host =
let doc = "Host to bind to" in
Arg.(value & opt string "0.0.0.0" & info ["H"; "host"] ~doc ~docv:"HOST")
(* setup log level จาก --verbose flag *)
let setup_log =
let docs = Manpage.s_common_options in
Term.(const (fun style_renderer level ->
Fmt_tty.setup_std_outputs ?style_renderer ();
Logs.set_level level;
Logs.set_reporter (Logs_fmt.reporter ()))
$ Fmt_cli.style_renderer ~docs ()
$ Logs_cli.level ~docs ())
let serve =
let info = Cmd.info "serve" ~doc:"Start the server" in
Cmd.v info Term.(ret (const serve_cmd $ port $ host $ setup_log))
let () =
let info = Cmd.info "mytool" ~version:"0.1.0" ~doc:"Systems demo" in
exit (Cmd.eval (Cmd.group info [serve]))
(* การใช้งาน:
./mytool serve --port 9090 --host 127.0.0.1 --verbose
./mytool serve --help *)
| Package | หน้าที่ |
|---|---|
cmdliner |
Declarative CLI + man page generation |
logs |
Structured logging (hierarchical sources) |
fmt |
Printing combinators + color |
bos |
Unix command-line tool utilities (paths, env, exec) |
daemonize |
Daemonization (fork + session detach) |
| Package | ประเภท | ใช้สำหรับ |
|---|---|---|
alcotest |
Unit testing | Standard unit tests, colorful output |
alcotest-eio |
Eio-aware testing | Async tests สำหรับ Eio |
qcheck |
Property-based testing | Random input generation + shrinking |
qcheck-alcotest |
QCheck + Alcotest integration | รวมสอง framework เข้าด้วยกัน |
bechamel |
Statistical benchmarking | Modern replacement ของ core_bench |
crowbar |
Fuzzing (AFL-based) | Coverage-guided fuzzing |
monolith |
Differential testing | ทดสอบ implementation ใหม่เทียบ reference |
ortac |
Runtime verification | Gospel specs → runtime checks |
| Package | หน้าที่ |
|---|---|
ctypes |
Type-safe C FFI — ไม่ต้องเขียน C stubs |
ctypes-foreign |
Dynamic loading (dlopen-like) |
integers |
Portable C-compatible integer types |
bigstringaf |
Zero-copy bigstring operations |
lru |
LRU cache (pure OCaml) |
รายการแหล่งเรียนรู้ที่ curated และจัดลำดับตามความเหมาะสมสำหรับนักพัฒนา systems ที่ต้องการเข้าใจ OCaml 5 อย่างลึกซึ้ง แบ่งตามประเภท (หนังสือ, เอกสารทางการ, blog, community) พร้อมคำแนะนำลำดับการอ่าน
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#3c3836','primaryTextColor':'#ebdbb2','primaryBorderColor':'#83a598','lineColor':'#a89984','secondaryColor':'#504945','background':'#282828','mainBkg':'#3c3836','nodeBorder':'#fe8019','clusterBkg':'#32302f','clusterBorder':'#fabd2f','edgeLabelBackground':'#282828'}}}%%
flowchart LR
subgraph Era1["ยุคต้น (Early Era) 1996-2010"]
A1[OCaml 1.0
1996]
A2[OCaml 3.x
2000s
Polymorphic variants]
A3[Jane Street
adopts OCaml
~2003]
A1 --> A2 --> A3
end
subgraph Era2["ยุค Modern Tooling 2011-2018"]
B1[opam 1.0
2013]
B2[Real World OCaml
2013]
B3[jbuilder → dune
2017]
B4[MirageOS
unikernel
~2013]
B5[Tezos launch
2018]
B1 --> B2 --> B3
B3 --> B4
B3 --> B5
end
subgraph Era3["ยุค Multicore (OCaml 5) 2019-ปัจจุบัน"]
C1[Multicore OCaml
research
KC Sivaramakrishnan]
C2[OCaml 5.0
Dec 2022
Domains + Effects]
C3[Eio 0.x
2022-2023]
C4[OCaml 5.1-5.3
performance fixes]
C5[OCaml 5.x
production ready]
C1 --> C2 --> C3 --> C4 --> C5
end
Era1 --> Era2 --> Era3
อันดับที่ 1 — Real World OCaml, 2nd Edition
dev.realworldocaml.org (2nd edition)อันดับที่ 2 — OCaml from the Very Beginning / More OCaml
อันดับที่ 3 — OCaml Programming: Correct + Efficient + Beautiful
cs3110.github.io/textbook/รายการ URL ที่ควร bookmark ไว้สำหรับงาน systems ใน OCaml 5:
ocaml.org/manual/5.x/ — Official language manual, รวม GC internals และ FFIocaml.org/docs/platform — OCaml Platform: tools ที่แนะนำอย่างเป็นทางการv3.ocaml.org/p/eio/latest/doc/index.html — Eio official documentationv3.ocaml.org/p/domainslib/ — Domainslib documentationgithub.com/ocaml-multicore/ocaml-multicore/wiki — Multicore OCaml wikigithub.com/ocaml/RFCs — Language evolution RFCstarides.com/blog) — บริษัทที่ drive OCaml 5 development; โพสต์เชิงลึกเรื่อง Multicore, MirageOS, Irminblog.janestreet.com) — Production OCaml ในงาน trading; insights เกี่ยวกับ performance และ large-scale designkcsrk.info) — ผู้นำทีม Multicore OCaml; อ่านเพื่อเข้าใจการออกแบบเชิง academicanil.recoil.org) — MirageOS founder; systems + OCamlalan.petitepomme.net/cwn/) — สรุปกิจกรรมในชุมชนรายสัปดาห์[ocaml] tag — สำหรับ Q&A แบบเฉพาะเจาะจงการอ่าน production code เป็นวิธีที่ดีที่สุดวิธีหนึ่งในการเรียน idiomatic style
| โปรเจกต์ | Domain | เหตุผลที่ควรอ่าน |
|---|---|---|
| Tezos / Octez | Blockchain node | ใช้ Lwt extensively, modular design, crypto |
| MirageOS | Unikernel | Pure OCaml stack ตั้งแต่ TCP/IP ถึง TLS |
| Irmin | Distributed store | Elegant abstraction, functor pattern ที่ดี |
| Dune | Build system | Large OCaml project, concurrent builds |
| Semgrep | Static analysis | AST manipulation, parser integration |
| Xen Storage | VM management | Low-level systems via OCaml |
| Coq / Rocq | Proof assistant | Complex type system usage (note: moving to different approach) |
สำหรับผู้ที่ต้องการเข้าใจ design decisions ในระดับลึกที่สุด:
สำหรับผู้ที่มีพื้นฐานภาษาโปรแกรมอื่นมาแล้ว (เช่น มาจาก Rust, Python, หรือ Go) ลำดับการเรียนที่แนะนำคือ:
cmdliner + logsQuick reference card สำหรับ syntax ที่ใช้บ่อยในงาน systems — ออกแบบให้พิมพ์ออกมา A4 เดียวแล้วติดไว้ข้างจอ
(* Sum type (variant) — สำหรับ state machine *)
type connection_state =
| Disconnected
| Connecting of { started_at : float }
| Connected of { fd : Unix.file_descr; peer : string }
| Closed of { reason : string }
(* Product type (record) — สำหรับ data bundle *)
type server_config = {
host : string;
port : int;
max_connections : int;
mutable running : bool; (* mutable field *)
}
(* Generalized Algebraic Data Type (GADT) — สำหรับ type-safe DSL *)
type _ expr =
| Int : int -> int expr
| Bool : bool -> bool expr
| Add : int expr * int expr -> int expr
| If : bool expr * 'a expr * 'a expr -> 'a expr
(* Polymorphic variant — flexible โดยไม่ต้องประกาศ type *)
let handle = function
| `Ok v -> Printf.printf "ok: %d\n" v
| `Error e -> Printf.eprintf "err: %s\n" e
| `Timeout -> prerr_endline "timeout"
(* Module signature — interface ที่ชัดเจน *)
module type STORE = sig
type key
type value
type t
val create : unit -> t
val put : t -> key -> value -> unit
val get : t -> key -> value option
end
(* Concrete implementation *)
module StringStore : STORE with type key = string and type value = string = struct
type key = string
type value = string
type t = (string, string) Hashtbl.t
let create () = Hashtbl.create 16
let put tbl k v = Hashtbl.replace tbl k v
let get tbl k = Hashtbl.find_opt tbl k
end
(* Functor — parameterized module *)
module Make_Cache (S : STORE) = struct
type t = { store : S.t; mutable hits : int; mutable misses : int }
let create () = { store = S.create (); hits = 0; misses = 0 }
let lookup c k = match S.get c.store k with
| Some v -> c.hits <- c.hits + 1; Some v
| None -> c.misses <- c.misses + 1; None
end
(* === Domains — true parallelism === *)
let d = Domain.spawn (fun () -> heavy_compute ())
let result = Domain.join d
(* === Atomic — lock-free counter === *)
let counter = Atomic.make 0
let () = Atomic.incr counter
let v = Atomic.get counter
let success = Atomic.compare_and_set counter 0 100
(* === Mutex — traditional locking === *)
let m = Mutex.create ()
let () =
Mutex.lock m;
(* critical section *)
Mutex.unlock m
(* === Eio — structured concurrency === *)
open Eio.Std
let () = Eio_main.run @@ fun env ->
Switch.run @@ fun sw ->
Fiber.fork ~sw (fun () -> worker_a ());
Fiber.fork ~sw (fun () -> worker_b ())
(* ทุก fiber ภายใต้ switch จะจบก่อน run คืน *)
(* === Effect handlers === *)
type _ Effect.t += Log : string -> unit Effect.t
let log msg = Effect.perform (Log msg)
let run_with_logging f =
Effect.Deep.try_with f ()
{ effc = fun (type a) (e : a Effect.t) ->
match e with
| Log msg -> Some (fun (k : (a, _) Effect.Deep.continuation) ->
Printf.printf "[LOG] %s\n" msg;
Effect.Deep.continue k ())
| _ -> None }
(* === Option — absence of value === *)
let safe_div a b = if b = 0 then None else Some (a / b)
let result = match safe_div 10 2 with
| Some v -> Printf.sprintf "ok: %d" v
| None -> "division by zero"
(* === Result — with error info === *)
let parse_int s =
try Ok (int_of_string s)
with Failure _ -> Error (Printf.sprintf "cannot parse: %s" s)
(* === Binding chain (monadic style) === *)
let ( let* ) = Result.bind
let compute s1 s2 =
let* a = parse_int s1 in
let* b = parse_int s2 in
if b = 0 then Error "div by zero"
else Ok (a / b)
(* === Exception (เมื่อ error ไม่ควร recover) === *)
exception Config_not_found of string
let load_config () =
if not (Sys.file_exists "config.toml") then
raise (Config_not_found "config.toml");
(* ... *)
OCaml 5 เป็นภาษาที่เหมาะกับ systems development ในบริบทที่ต้องการ type safety ระดับสูง, GC ที่ predictable, และ concurrency model ที่ทันสมัย ไม่ใช่ทุกสถานการณ์จะเหมาะกับ OCaml และการรู้ว่าเมื่อไหร่ควรเลือกเครื่องมือใดคือความสามารถที่สำคัญของ systems engineer
เลือก OCaml 5 เมื่อ:
พิจารณาภาษาอื่นเมื่อ:
OCAMLRUNPARAM="b" เพื่อให้ได้ backtrace ใน production logs-rectypes หรือ -strict-sequence ตามต้องการ + เปิด warnings เป็น errors (-warn-error +a)opam lock เพื่อสร้าง reproducible buildperf + landmarks ก่อน deploy เพื่อหา allocation hotspotsminor_heap_size, space_overhead) ตาม workload — default อาจไม่เหมาะMEMTRACE=./trace.ctf ./myappGc.stat () เป็น metricsRestart=on-failure + resource limitsmusl เพื่อลด runtime dependency บน target hostลงท้าย: OCaml 5 เปิดประตูสู่การใช้ภาษา functional ที่ type-safe สำหรับงาน systems ที่ต้องการ parallelism จริงๆ เป็นครั้งแรก การเรียนรู้โมเดล Domains + Effects + Eio ต้องใช้เวลา แต่สิ่งที่ได้กลับคือโค้ดที่สั้น ชัดเจน และพิสูจน์ได้ง่าย — คุณสมบัติที่ systems engineer มองหามาตลอด
หวังว่าภาคผนวกนี้จะเป็น reference ที่ใช้ได้จริงสำหรับทั้งการเริ่มต้นและการทำงานประจำวัน