OCaml

OCaml（əl əl），是一個函數式、指令式、模塊化、面向對象的通用的編程語言。在Xavier Leroy和Damien Doligez，於1990年和1991年實現的ML方言Caml Light之上，Didier Rémy和Jérôme Vouillon，於1996年增加了面向對象特徵archive-url=https://web.archive.org/web/20220121031810/http://gallium.inria.fr/~remy/ftp/objective-ml!popl97.pdfarchive-url=https://web.archive.org/web/20220120081109/https://caml.inria.fr/pub/papers/remy_vouillon-objective_ml-tapos98.pdf，從而形成了“Objective Caml”，在2011年時重命名為“OCaml”In 1990 and 1991, Xavier Leroy designed a completely new implementation of Caml, based on a bytecode interpreter written in C. Damien Doligez provided an excellent memory management system. ……In 1995, Xavier Leroy released Caml Special Light, which improved over Caml Light in several ways. In 1995, Xavier Leroy released Caml Special Light, which improved over Caml Light in several ways. First, an optimizing native-code compiler was added to the bytecode compiler. ……Second, Caml Special Light offered a high-level module system, designed by Xavier Leroy and inspired by the module system of Standard ML. ……Didier Rémy, later joined by Jérôme Vouillon, designed an elegant and highly expressive type system for objects and classes. This design was integrated and implemented within Caml Special Light, leading to the Objective Caml language and implementation, first released in 1996 and renamed to OCaml in 2011.|access-date=2021-09-02|archive-date=2022-04-13|archive-url=https://web.archive.org/web/20220413084146/https://caml.inria.fr/about/history.en.html。
OCaml工具鏈包括交互式頂層解釋器、字節碼編譯器、優化的本機代碼編譯器，可逆調試器和一個包管理器（OPAM）。OCaml最初開發於自動定理證明的場景中，並在靜態分析和形式方法軟件中有超凡的存在感。此外，它在系統編程、網頁編程和金融工程及其他應用領域都有嚴肅的應用。
歷史上，Ascánder Suárez於1987年基於Guy Cousineau的Categorical abstract machine（CAM）archive-url=https://web.archive.org/web/20220128115221/http://michel.mauny.net/data/papers/mauny-suarez-1986.pdf LFP '86: Proceedings of the 1986 ACM conference on LISP and functional programming, Pages 266–278.，重新實現了Gérard Huet早先的ML方言archive-url=https://web.archive.org/web/20220129103018/http://www.nuprl.org/crash/eventml/KreitzandRahli-ClassicML.pdf，並用“範疇抽象機語言”的首字母簡寫將其命名為Camlarchive-url=https://web.archive.org/web/20220406231614/https://hal.inria.fr/inria-00070046/document Research Report RT-0121, INRIA. pp.491.，Caml Light放棄了這個抽象機器又進行了重新實現。OCaml是開放源代碼項目，此項目的管理和大部分維護工作，已經交由法國國家信息與自動化研究所（INRIA）。在2000年代早期，來自OCaml的元素被很多語言接納，特別是F#和Scala。

**哲學**
ML派生語言最著稱的是靜態類型系統和類型推論編譯器。OCaml將函數式、指令式和麵向對象編程統一於類ML的類型系統之下。因此編程者不需要為了使用OCaml而非常熟悉純函數式編程範型。
通過要求編程者在靜態類型系統的約束下工作，OCaml消除了關聯於動態類型語言的很多有關於類型的運行時間問題。還有，OCaml的類型推論編譯器，極大的減少了在多數靜態類型語言中對手工類型標註的需要。例如，變量的數據類型和函數的簽名，通常不需要像Java和C#語言中那樣顯式的聲明，因為它們可以從應用於這個變量和代碼中其他值的算符和其他函數推論出來。有效的使用OCaml的類型系統可能要求一個編程者面對一些複雜性，但是這種規矩能得到可靠的、高性能軟件作為回報。
OCaml與源於學術界的其他語言的最顯著區別可能是強調了性能。它的靜態類型系統防止了運行時間類型不匹配，從而排除了動態類型語言運行時間類型和安全檢查的性能負擔，卻在除了關閉數組邊界檢查，和使用一些類型不安全特徵比如序列化之外的情況下，仍能保證運行時間安全性。這些運行時間檢查需求足夠罕見，在實踐中完全可以避免。
在類型檢查開銷之外，函數式編程語言，要編譯成高效的機器語言代碼，由於如funarg problem這樣的要點，一般而言是具有挑戰性的。與標準的循環、寄存器和指令優化一起，OCaml的優化編譯器採用靜態程序分析方法，來優化值包裝（boxing）和閉包分配，幫助結果代碼得到最大化的性能，即使它大量使用了函數式編程構造。
曾經宣稱：“OCaml至少提供了像樣的C編譯器的50%的性能”，儘管直接比較是不可能的。在OCaml標準庫中的一些函數，是採用比在其他語言標準庫中等價的函數更快的算法實現的。例如，在OCaml標準庫中集合並集的實現，在理論上比指令式語言（例如C++、Java）的標準庫中的等價函數，要漸進性的更快，因為OCaml實現利用了集合的不可變性，而在輸出中重用了輸入集合的一些部份（參見可持久化數據結構）。

**特徵**
OCaml的特徵包括：靜態類型系統、類型推論、參數多態、尾遞歸、模式匹配、頭等詞法閉包、函子（參數化模塊）、例外處理和增量分代自動垃圾回收。
OCaml著稱於將ML風格類型推論，擴展到通用語言中的對象系統。這允許了結構子類型，這裡的對象類型是兼容的，如果它們的方法簽名是兼容的，不用管它們聲明的繼承，這是在靜態類型語言中不尋常的特徵。
OCaml提供了鏈接到C原語的foreign function interface，包括了兼容於C和Fortran二者格式的對高效數值數組的語言支持。OCaml還支持建立可以鏈接到用C寫的main程序的OCaml函數庫。
OCaml發行包含了：- 詞法分析和語法解析工具分別叫做ocamllex和ocamlyacc。- 支持步進回溯的調試器用來調查錯誤。- Documentation generator。- 剖析器用來測量性能。- 很多通用函數庫。
本機代碼編譯器可以在很多平臺上獲得，包括Unix、Microsoft Windows和Apple macOS。可移植性是通過至此主要架構的本機代碼生成實現的：IA-32、X86-64（AMD64）、Power、RISC-V、ARM和ARM64。
OCaml字節碼和本機代碼程序可以用多線程風格書寫，具有搶佔式上下文切換。但是由於當前唯一可得的語言完全實現INRIA OCaml的垃圾回收器，不是為併發性而設計的，對稱多處理是不支持的。在相同進程中的OCaml線程只能分時執行。但是有一些分佈式計算庫比如Functory和Plasma。

**代碼例子**
OCaml的代碼片段可以通過鍵入到頂層REPL中來很容易的研習。這是一個交互式的OCaml會話，它打印結果或定義的表達式的推論出的類型。OCaml頂層可以通過簡單執行OCaml程序來啟動：
$ ocaml
OCaml version 5.2.0
Enter #help;; for help.
# 
可以接著在#提示符處鍵入代碼。例如計算1+2*3:
# 1 + 2 * 3;;
- : int = 7
OCaml推論出這個表達式的類型是int（機器精度整數）並給出結果7。

**Hello World**
下面的程序hello.ml:
print_endline "Hello World!"
可以被編譯成字節碼可執行文件：
$ ocamlc hello.ml -o hello
或者被編譯成優化的本地代碼可執行文件：
$ ocamlopt hello.ml -o hello
接著執行它：
$ ./hello
Hello World!
給ocamlc的第一個實際參數hello.ml，指定要編譯的源文件而-o hello標誌指定了輸出文件。

**階乘函數**
很多數學函數，比如階乘，可以很自然的表示為純粹的函數形式：
let rec fact n =
if n=0 then 1 else n * fact(n - 1);;
這個函數可以使用模式匹配等價的寫為：
let rec fact = function

0 -> 1

n -> n * fact(n - 1);;
後者形式是階乘作為遞推關係的數學定義。
編譯器將這個函數的類型推論為int -> int，意味著這個函數將int映射到int。例如，12!：
# fact 12;;
- : int = 479001600

**斐波那契序列**
下列代碼計算輸入數n的斐波那契數列。它使用了尾遞歸和模式匹配。
let fib n =
let rec fib_aux m a b =
match m with

0 -> a

_ -> fib_aux (m - 1) b (a + b)
in fib_aux n 0 1;;

**生日問題**
下列程序計算在一個屋子裡面有完全唯一的生日概率小於50%的最少人數，在生日問題中，對於1個人這個概率是365/365（或100%），對於2個人是364/365，對於3個人是364/365 × 363/365，最終答案是23個人：
let year_size = 365.
let rec birthday_paradox prob people =
let prob = (year_size -. float people) /. year_size *. prob  in
if prob 
# birthday_paradox 1.0 1;;
answer = 23
- : unit = ()

**合計整數列表**
列表是OCaml中的基礎數據類型之一。下面的代碼例子定義遞歸函數sum，它接受一個實際參數integers，而它被假定為整數的列表。注意關鍵字rec指示了這個函數是遞歸的。這個函數遞歸的在給定整數列表之上進行迭代，並提供這些元素的一個總和。match語句類似於C的switch語句，但要更加一般性。
let rec sum integers =                   (* 關鍵字rec含義為遞歸。 *)
match integers with

-> 0                              (* 產生0，如果integers為空列表 。 *)

first :: rest -> first + sum rest;;  (* 遞歸調用，如果integers是非空列表；
first是這個列表的第一個元素，
而rest是餘下元素的列表，可能是。 *)
# sum 1;2;3;4;5;;
- : int = 15
另一種方式是對列表使用標準的fold高階函數:
let sum integers =
List.fold_left (fun accumulator x -> accumulator + x) 0 integers;;
# sum 1;2;3;4;5;;
- : int = 15
因為匿名函數是簡單的+算符應用，它可以簡寫為：
let sum integers =
List.fold_left (+) 0 integers
進一步的，還可以通過採用Partial application省略列表實際參數：
let sum =
List.fold_left (+) 0

**快速排序**
OCaml自身可提供對遞歸算法的簡介表達。下列代碼例子實現了類似於以升序排序一個列表的quicksort的一個算法：
let rec qsort = function

-> 

pivot :: rest ->
let is_less x = x 

**高階函數**
函數可以接受函數作為參數並且返回函數作為結果。例如，應用twice到函數f產生應用f到它的實際參數兩次的一個函數：
let twice (f : 'a -> 'a) = fun (x : 'a) -> f (f x);;
let inc (x : int) : int = x + 1;;
let add2 = twice inc;;
let inc_str (x : string) : string = x ^ " " ^ x;;
let add_str = twice(inc_str);;
# add2 98;;
- : int = 100
# add_str "Test";;
- : string = "Test Test Test Test"
函數twice使用類型變量'a，來指示它可以應用於映射一個類型'a到自身的任何f，而非只應用於int->int函數。特別是，twice甚至可以應用於自身：
# let fourtimes f = (twice twice) f;;
val fourtimes : ('a -> 'a) -> 'a -> 'a = 
# let add4 = fourtimes inc;;
val add4 : int -> int = 
# add4 98;;
- : int = 102

**邱奇數**
下列代碼定義自然數的邱奇編碼，具有後繼（succ）和加法（add）。邱奇數n是接受一個函數f和一個值x的一個高階函數，它應用f到x精確的n次：
let zero f x = x
let succ n f x = f (n f x)
let one = succ zero
let two = succ (succ zero)
let add n1 n2 f x = n1 f (n2 f x)
let to_string n = n (fun k -> "S" ^ k) "0";;
為了將一個邱奇數從函數值轉換成一個字符串，這裡把它傳遞給向其輸入和常量字符串"0"前置上字符串"S"的函數to_string：
# let _ = to_string (add (succ two) two);;
- : string = "SSSSS0"

**對象例子**
在OCaml中對象，通過它們的方法的名字和類型，按結構來確定類型。對象可以直接創建（立即對象），而不用通過有名稱的類。例如：
# let x =
object
val mutable x = 5
method get_x = x
method set_x y = x  unit > = 
這裡OCaml交互式運行時間系統打印出這個對象的推論類型。它的類型 unit >，只由它的方法來定義。換句話說，x的類型由方法類型get_x : int和set_x : int -> unit而非任何名字來定義。類只充當建立對象的函數，例如上例中的對象可以用類來定義，並接著用new算符來建立：
# class simple_cls =
object (self)
val mutable x = 5
method get_x = x
method set_x y = x 
要定義有相同的方法和方法類型的另一個對象：
# let y =
object
method get_x = 2
method set_x y = Printf.printf "%d\n" y
end;;
val y :  unit > = 
OCaml將它們視為有相同的類型。例如，等式算符被確定類型為只接受有相同類型的兩個值：
# x = y;;
- : bool = false
所有儘管它們有不同的值，卻必定是相同類型的，否則連類型檢查都不會做完。這展示了類型等價是結構性的。可以定義調用一個方法的函數：
# let set_to_10 a = a#set_x 10;;
val set_to_10 :  'a; .. > -> 'a = 
第一個實際參數的推論類型 'a; .. >是值得關注的。..意味著第一個實際參數，可以是有接受一個int作為實際參數的set_x方法的任何對象。所以它可以用在對象x之上：
# set_to_10 x;;
- : unit = ()
另一個對象可以碰巧有這個方法和方法類型；其他方法是無關緊要的：
# let z =
object
method blahblah = 2.5
method set_x y = Printf.printf "%d\n" y
end;;
val z :  unit > = 
set_to_10函數對它也有效：
# set_to_10 z;;
10
- : unit = ()
這展示了對於事物比如方法調用的兼容性是由結構來確定的。下面為只有一個get_x方法而沒有其他方法的對象定義一個類型同義詞（synonym）：
# type simpler_obj = ;;
type simpler_obj = 
對象x不是這個類型的；但在結構上x是這個類型的一個子類型，因為x包含它的方法的一個超集。所以x可以強制（coerce）成這個類型：
# (x :> simpler_obj);;
- : simpler_obj = 
# (x :> simpler_obj)#get_x;;
- : int = 10
但是對象z不行，因為它不是一個結構子類型：
# (z :> simpler_obj);;
Error: Type  unit > is not a subtype of
simpler_obj =  
The first object type has no method get_x
這展示了拓寬強制的兼容性是結構性的。

**任意精度階乘函數**
可以從OCaml訪問各種各樣的庫。比如，OCaml有內建的任意精度算術。由於階乘函數增長得非常迅速，會很快溢出機器精度的整數。因此階乘很適合選用任意精度算術。
在OCaml中，Num模塊（現在被ZArith所取代）提供了任意精度算術，比如在Ubuntu中安裝它：sudo apt install libnum-ocaml-dev，它可以如下這樣裝載到運行中的頂層中：
#use "topfind";;
#require "num";;
open Num;;
階乘函數可以使用任意精度算符/、*/和-/寫為：
let rec fact n =
if n =/ Int 0 then Int 1 else n */ fact(n -/ Int 1);;
這個函數可以計算非常大的階乘比如120!：
# string_of_num (fact (Int 120));;
- : string =
"6689502913449127057588118054090372586752746333138029810295671352301633557244962989366874165271984981308157637893214090552534408589408121859898481114389650005964960521256960000000000000000000000000000"

**繪製圖形例子**
下列程序simple.ml使用OpenGL呈現一個緩慢旋轉的2D三角形:
let () =
ignore (Glut.init Sys.argv);
Glut.initDisplayMode ~double_buffer:true ();
ignore (Glut.createWindow ~title:"OpenGL Demo");
let angle t = 10. *. t *. t in
let render () =
GlClear.clear  `color ;
GlMat.load_identity ();
GlMat.rotate ~angle: (angle (Sys.time ())) ~z:1. ();
GlDraw.begins `triangles;
List.iter GlDraw.vertex2 -1., -1.; 0., 1.; 1., -1.;
GlDraw.ends ();
Glut.swapBuffers () in
GlMat.mode `modelview;
Glut.displayFunc ~cb:render;
Glut.idleFunc ~cb:(Some Glut.postRedisplay);
Glut.mainLoop ()
需要事先安裝負責綁定到OpenGL的LablGL，比如在Ubuntu中安裝它：sudo apt install liblablgl-ocaml-dev，這個程序可以如下這樣編譯成字節碼：
$ ocamlc -I +lablGL lablglut.cma lablgl.cma simple.ml -o simple
或編譯成本機代碼：
$ ocamlopt -I +lablGL lablglut.cmxa lablgl.cmxa simple.ml -o simple
或者簡單的使用ocamlfind建造命令：
$ ocamlfind opt simple.ml -package lablgl.glut -linkpkg -o simple
然後運行：
$ ./simple
可以使用OCaml開發非常複雜、高性能的2D和3D圖形程序。使用OpenGL和OCaml，結果的程序可以跨平臺編譯而在主要平臺上無需改動。

**用OCaml寫成的程序**
 Coq，一個形式證明輔助器。- HOL Light，一個形式證明輔助器。- Coccinelle，用於C程序源代碼變換的實用工具。- Frama-C，分析C程序的一個框架。- Flow，Facebook創建的用於推論和檢查JavaScript的靜態類型的一個靜態分析器。- Infer，Facebook創建的用於Java、C、C++和Objective-C的靜態分析器，用來檢測iOS和Android應用中的缺陷。- pyre-check，Facebook創建的用於Python的類型檢查器。- Hack編程語言編譯器，Facebook創建，擴展PHP具有靜態類型。- Haxe編程語言編譯器。- WebAssembly參考解釋器，它是意圖在web瀏覽器內執行的低層字節碼。- FFTW，C的FFT庫，它的多數性能關鍵代碼是由用OCaml寫的一個程序生成的。- Owl Scientific Computing，用於科學和工程計算的專用系統。- MirageOS，用純OCaml寫的一個單一內核編程框架。- Ocsigen，一個OCaml的web應用框架。- Opa，用於Web開發的自由和開源的一個編程語言。- 0install，一個多平臺包管理器。- MLDonkey，一個多網絡P2P程序。- Unison，一個文件同步器。

**參見**
 Caml，OCaml的先驅語言。- F#，由MSR開發，是一個基於OCaml的一個以.NET為目標的編程語言。- F*，由MSR和INRIA主導開發，是一個基於OCaml的依賴類型函數式程序語言。- Reason，OCaml的語法擴展和工具鏈，也可以轉譯成JavaScript。

**有關書籍**
 archive-url=https://web.archive.org/web/20220514090626/https://dev.realworldocaml.org/- archive-url=https://web.archive.org/web/20220223135753/https://caml.inria.fr/pub/docs/oreilly-book/ocaml-ora-book.pdf- archive-url=https://web.archive.org/web/20210910112045/https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.132.9566&rep=rep1&type=pdf

**引用**

**外部鏈接**
  OCaml manual date=20220404034329 -  OCaml Package Manager date=20220512061740 -  Awesome OCaml date=20220418072759