位元詩人 OCaml 程式設計入門

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

當前定位

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 函式庫

BaseCore 是由 Jane Street 維護的非官方標準函式庫。
Jane Street 是一家以 OCaml 為主要技術棧的量化交易公司,並長期投入資源維護這些函式庫。

雖然它們並非 OCaml 官方標準庫,但在社群中使用極為廣泛,幾乎已成為事實上的標準。
目前主要分為以下部分:

  • Base:精簡的基礎函式庫
  • Core:功能更完整的大型基礎函式庫
  • Core_unix:依賴 POSIX 環境的 Core 擴充庫

在程式設計上,使用 Base 與 Core 可能因 API 差異導致程式碼略有不同。
然而,這兩者在社群中的地位極高,因此以 Base/Core 為基礎撰寫 OCaml 程式是可接受的做法。

原始碼命名規範

檔案名稱

建議使用 小寫英文字母加底線 的組合,這樣最不容易產生相容性問題。

副檔名

常見的副檔名如下:

  • .ml:OCaml 程式碼檔
  • .mli:介面檔 (signature file)
  • .mllocamllex 描述檔,用於撰寫 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 程式設計:

關於作者

位元詩人 (ByteBard) 是資訊領域碩士,喜歡用開源技術來解決各式各樣的問題。這類技術跨平台、重用性高、技術生命長。

除了開源技術以外,位元詩人喜歡日本料理和黑咖啡,會一些日文,有時會自助旅行。

近期在學習韓文,並將語言學習的心得轉化為開源專案,回饋社群。

這裡是位元詩人的 GitHub 個人頁