當前定位
OCaml 是一種 通用型靜態編譯語言,核心特色在於支援函數式程式設計。目前它的主要應用領域包括 定理證明、語言工具開發,以及 金融工程。
在執行層面上,OCaml 的二進位檔會附帶 Runtime,因此並不適合用來製作動態函式庫。然而,它內建 垃圾回收機制 (GC),使得 OCaml 在大多數應用程式開發中表現良好,除了需要嚴格即時性的系統程式外,都能勝任。
Hello World
建立文字檔案 hello.ml ,撰寫以下內容:
let _ = Printf.printf "%s\n" "Hello World"
使用 OCaml 編譯器編譯程式碼後執行程式:
$ ocamlopt -o hello hello.ml
$ ./hello
Hello World
多檔案編譯
建立文字檔案 utils.ml ,撰寫以下內容:
let square x = x * x
建立文字檔案 utils.mli ,撰寫以下內容:
val square : int -> int
剛開始學 OCaml 時,會覺得界面檔 (.mli) 不容易撰寫。這是對 OCaml 型態系統不熟造成的,屬正常現象,寫一陣子 OCaml 程式就會改善。
建立文字檔案 main.ml ,撰寫以下內容:
open Utils
let _ = assert(9 = (Utils.square 3))
編譯後執行程式:
$ ocamlopt -o main utils.mli utils.ml main.ml
$ ./main
注意程式碼的順序是固定的,順序錯誤會導致編譯失敗。
Base 與 Core 函式庫
Base 與 Core 是由 Jane Street 維護的非官方標準函式庫。
Jane Street 是一家以 OCaml 為主要技術棧的量化交易公司,並長期投入資源維護這些函式庫。
雖然它們並非 OCaml 官方標準庫,但在社群中使用極為廣泛,幾乎已成為事實上的標準。
目前主要分為以下部分:
- Base:精簡的基礎函式庫
- Core:功能更完整的大型基礎函式庫
- Core_unix:依賴 POSIX 環境的 Core 擴充庫
在程式設計上,使用 Base 與 Core 可能因 API 差異導致程式碼略有不同。
然而,這兩者在社群中的地位極高,因此以 Base/Core 為基礎撰寫 OCaml 程式是可接受的做法。
原始碼命名規範
檔案名稱
建議使用 小寫英文字母加底線 的組合,這樣最不容易產生相容性問題。
副檔名
常見的副檔名如下:
.ml:OCaml 程式碼檔.mli:介面檔 (signature file).mll:ocamllex描述檔,用於撰寫 lexer.mly:Menhir 描述檔,用於撰寫 parser.c:C 語言原始碼。OCaml 可與 C 混合編程.o/.obj:目的檔 (object file),編譯產生的 artifact.exe:Windows 二進位執行檔- Unix 二進位執行檔通常沒有副檔名
此外,編譯過程中也會產生一些暫存檔副檔名,這裡不逐一列出。
主函式
在 OCaml 中並沒有「主函式」的正式概念,程式會依照原始碼的順序 自上而下執行。
依照社群慣例,通常會在主程式的最下方撰寫:
let () = ...
或是:
let _ = ...
這樣的寫法相當於一個「入口點」,用來權充主函式,讓程式結構更清晰。
分號的用途
在 OCaml 中,分號 (;) 並非單純的語法裝飾,而是有特殊意義:
-
單分號 (
;)
用來區隔多個表達式。
由於 OCaml 是以「表達式」為核心的語言,分號能讓多行程式碼依序執行。 -
雙分號 (
;;)
常見於互動式環境(REPL /utop),用來明確結束一段程式碼並立即執行。
它的效果類似於在程式中寫let _ = ...,表示執行但不需要回傳值。
表達式
和其他函數式程式語言一樣,OCaml 是以 表達式 (expression) 為核心。
許多在主流語言中被視為「敘述 (statement)」的語法,在 OCaml 中其實都是表達式,並且可以回傳值。
範例
let x = if true then 1 else 2
在這裡,if ... then ... else ... 並不是單純的控制敘述,而是一個能回傳整數的表達式。因此 x 會被綁定為 1。
大小寫敏感
OCaml 程式碼是 大小寫敏感 (case-sensitive) 的。
依照社群慣例,大部分識別字會使用 小寫字母,但在 模組名稱 的首字母則需大寫。
範例
module MyModule = struct
let value = 42
end
let x = MyModule.value
在這裡,MyModule 的首字母必須大寫,否則編譯器會視為一般識別字而非模組。
排版元素
除了必要的空白外,OCaml 對程式碼的排版相對自由。
依照社群慣例,縮排通常使用兩格空白,而非四格。
範例
let add x y =
x + y
這樣的縮排方式在 OCaml 社群中最常見,能保持程式碼簡潔且易讀。
註解
在 OCaml 中,註解使用一對 (* 與 *) 將文字包起來。
這種方式可以嵌入任意自由文字,編譯器會忽略其中的內容。
範例
(* 這是一個單行註解 *)
let add x y =
x + y (* 這裡也可以加註解 *)
OCaml 的註解是 可巢狀 (nested) 的,這意味著你可以在註解中再放一個註解:
(* 外層註解
(* 內層註解 *)
*)
系統資源處理
OCaml 內建 垃圾回收器 (Garbage Collector, GC),因此程式設計者不需要手動釋放記憶體。
此外,OCaml 不支援指標運算,避免了低階記憶體操作的複雜性與風險。
這些特性使得 OCaml 特別適合用來撰寫 應用程式,因為開發者能專注於程式邏輯而非資源管理。
然而,正因為缺乏直接的指標操作與嚴格的即時控制,OCaml 不適合用於系統程式或動態函式庫的開發。
Rust
Rust 是一種具備 ML 家族特性的 系統程式語言。
雖然在語法上與 ML 語言存在差異,但它同時兼具 函數式程式設計的特色 與 系統語言的低階能力,因此成為目前少數能跨越兩者領域的開發工具。
如果你已經習慣 OCaml 的設計哲學,但需要撰寫 系統程式、延伸模組 或 動態函式庫,那麼 Rust 會是一個值得關注的選擇。
OCaml 與 Rust 的比較
雖然 OCaml 與 Rust 在定位上不同,但它們在語言設計上有互補性。以下是常見的比較面向:
| 面向 | OCaml | Rust |
|---|---|---|
| 語言家族 | ML 家族,偏向函數式程式設計 | 系統語言,融合 ML 特性 |
| 記憶體管理 | 內建垃圾回收器 (GC),自動管理資源 | 所有權 (Ownership) 與生命週期 (Lifetime) 機制,無 GC |
| 指標操作 | 不支援指標運算 | 支援安全指標與低階操作 |
| 適用領域 | 應用程式、語言工具、定理證明、金融工程 | 系統程式、延伸模組、動態函式庫 |
| 即時性需求 | 不適合嚴格即時系統 | 可用於即時系統,提供低階控制 |
| 語法風格 | 強調表達式導向,簡潔偏函數式 | 結合命令式與函數式,語法較嚴謹 |
小提示 (Tips)
- 如果你偏好 快速開發應用程式,OCaml 會是更輕鬆的選擇。
- 如果你需要 系統層級的控制,Rust 則能提供更強的安全性與效能。
- 兩者的哲學其實相互呼應:OCaml 強調抽象與安全,Rust 則在低階系統中延續這種安全設計。
學習建議
如果你同時關注 OCaml 與 Rust,可以考慮以下學習路線:
-
先學 OCaml
熟悉函數式程式設計的思維模式,例如「所有東西都是表達式」與強型別系統。
這會讓你在理解 Rust 的所有權模型時更自然,因為你已經習慣抽象與安全的設計哲學。 -
再學 Rust
在掌握 OCaml 的抽象思維後,轉向 Rust 的系統程式設計。
Rust 的所有權與生命週期機制能補足 OCaml 在系統層級的不足,讓你能同時寫應用程式與系統程式。
小提示 (Tips)
- OCaml 幫助你建立「高階抽象」的思維。
- Rust 則讓你在「低階系統」中維持安全性。
- 兩者結合,能讓你在程式設計上同時具備 抽象力 與 系統控制力。
另見
以下是站長自製的工具腳本,用來輔助 OCaml 程式設計:
- ocaml-clean-compile:編譯時不會在當前目錄殘留暫存檔
- clean-artifact:清除暫存檔和 Artifact