OCaml

OCaml，是一个函数式、指令式、模块化、面向对象的通用的编程语言。在和，于1990年和1991年实现的ML方言Caml Light之上，Didier Rémy和Jérôme Vouillon，于1996年增加了面向对象特征</ref>，从而形成了“Objective Caml”，在2011年时重命名为“OCaml”。
OCaml工具链包括交互式顶层解释器、字节码编译器、优化的本机代码编译器，可逆调试器和一个包管理器（OPAM）。OCaml最初开发于自动定理证明的场景中，并在静态分析和形式方法软件中有超凡的存在感。此外，它在系统编程、网页编程和金融工程及其他应用领域都有严肃的应用。
历史上，Ascánder Suárez于1987年基于的（CAM） LFP '86: Proceedings of the 1986 ACM conference on LISP and functional programming, Pages 266–278.</ref>，重新实现了早先的ML方言</ref>，并用“范畴抽象机语言”的首字母简写将其命名为Caml Report RT-0121, INRIA. pp.491.</ref>，Caml Light放弃了这个抽象机器又进行了重新实现。OCaml是开放源代码项目，此项目的管理和大部分维护工作，已经交由法国国家信息与自动化研究所（INRIA）。在2000年代早期，来自OCaml的元素被很多语言接纳，特别是F#和Scala。
哲学.
ML派生语言最著称的是静态类型系统和类型推论编译器。OCaml将函数式、指令式和面向对象编程统一于类ML的类型系统之下。因此编程者不需要为了使用OCaml而非常熟悉纯函数式编程范型。
通过要求编程者在静态类型系统的约束下工作，OCaml消除了关联于动态类型语言的很多有关于类型的运行时间问题。还有，OCaml的类型推论编译器，极大的减少了在多数静态类型语言中对手工类型标注的需要。例如，变量的数据类型和函数的签名，通常不需要像Java和C#语言中那样显式的声明，因为它们可以从应用于这个变量和代码中其他值的算符和其他函数推论出来。有效的使用OCaml的类型系统可能要求一个编程者面对一些复杂性，但是这种规矩能得到可靠的、高性能软件作为回报。
OCaml与源于学术界的其他语言的最显著区别可能是强调了性能。它的静态类型系统防止了运行时间类型不匹配，从而排除了动态类型语言运行时间类型和安全检查的性能负担，却在除了关闭数组边界检查，和使用一些类型不安全特征比如序列化之外的情况下，仍能保证运行时间安全性。这些运行时间检查需求足够罕见，在实践中完全可以避免。
在类型检查开销之外，函数式编程语言，要编译成高效的机器语言代码，由于如这样的要点，一般而言是具有挑战性的。与标准的循环、寄存器和指令优化一起，OCaml的优化编译器采用静态程序分析方法，来优化值（boxing）和闭包分配，帮助结果代码得到最大化的性能，即使它大量使用了函数式编程构造。
曾经宣称：“OCaml至少提供了像样的C编译器的50%的性能”，尽管直接比较是不可能的。在OCaml标准库中的一些函数，是采用比在其他语言标准库中等价的函数更快的算法实现的。例如，在OCaml标准库中集合并集的实现，在理论上比指令式语言（例如C++、Java）的标准库中的等价函数，要渐进性的更快，因为OCaml实现利用了集合的不可变性，而在输出中重用了输入集合的一些部份（参见可持久化数据结构）。
特征.
OCaml的特征包括：静态类型系统、类型推论、参数多态、尾递归、模式匹配、头等词法闭包、函子（参数化模块）、例外处理和增量分代自动垃圾回收。
OCaml著称于将ML风格类型推论，扩展到通用语言中的对象系统。这允许了，这里的对象类型是兼容的，如果它们的方法签名是兼容的，不用管它们声明的继承，这是在静态类型语言中不寻常的特征。
OCaml提供了链接到C原语的，包括了兼容于C和Fortran二者格式的对高效数值数组的语言支持。OCaml还支持建立可以链接到用C写的codice_1程序的OCaml函数库。
OCaml发行包含了：
本机代码编译器可以在很多平台上获得，包括Unix、Microsoft Windows和Apple macOS。可移植性是通过至此主要架构的本机实现的：IA-32、X86-64（AMD64）、、RISC-V、ARM和ARM64。
OCaml字节码和本机代码程序可以用多线程风格书写，具有抢占式上下文切换。但是由于当前唯一可得的语言完全实现INRIA OCaml的垃圾回收器，不是为并发性而设计的，对称多处理是不支持的。在相同进程中的OCaml线程只能分时执行。但是有一些分布式计算库比如Functory和Plasma。
代码例子.
OCaml的代码片段可以通过键入到顶层REPL中来很容易的研习。这是一个交互式的OCaml会话，它打印结果或定义的表达式的推论出的类型。OCaml顶层可以通过简单执行OCaml程序来启动：
<syntaxhighlight lang="console">
$ ocaml
OCaml version 5.2.0
Enter #help;; for help.
</syntaxhighlight>
可以接着在codice_4提示符处键入代码。例如计算codice_5:
<syntaxhighlight lang="OCaml" highlight="2">
- : int = 7
</syntaxhighlight>
OCaml推论出这个表达式的类型是codice_6（机器精度整数）并给出结果codice_7。
Hello World.
下面的程序codice_8:
<syntaxhighlight lang="OCaml">
print_endline "Hello World!"
</syntaxhighlight>
可以被编译成字节码可执行文件：
<syntaxhighlight lang="console">
$ ocamlc hello.ml -o hello
</syntaxhighlight>
或者被编译成优化的本地代码可执行文件：
<syntaxhighlight lang="console">
$ ocamlopt hello.ml -o hello
</syntaxhighlight>
接着执行它：
<syntaxhighlight lang="console">
$ ./hello
Hello World!
</syntaxhighlight>
给ocamlc的第一个实际参数codice_8，指定要编译的源文件而codice_10标志指定了输出文件。
阶乘函数.
很多数学函数，比如阶乘，可以很自然的表示为纯粹的函数形式：
<syntaxhighlight lang="OCaml">
let rec fact n =
</syntaxhighlight>
这个函数可以使用模式匹配等价的写为：
<syntaxhighlight lang="OCaml">
let rec fact = function
</syntaxhighlight>
后者形式是阶乘作为递推关系的数学定义。
编译器将这个函数的类型推论为codice_11，意味着这个函数将codice_6映射到codice_6。例如，codice_14：
<syntaxhighlight lang="OCaml" highlight="2">
- : int = 479001600
</syntaxhighlight>
斐波那契序列.
下列代码计算输入数codice_15的斐波那契数列。它使用了尾递归和模式匹配。
<syntaxhighlight lang = OCaml>
let fib n =
</syntaxhighlight>
生日问题.
下列程序计算在一个屋子里面有完全唯一的生日概率小于50%的最少人数，在生日问题中，对于1个人这个概率是365/365（或100%），对于2个人是364/365，对于3个人是364/365 × 363/365，最终答案是23个人：
<syntaxhighlight lang="OCaml">
let year_size = 365.
let rec birthday_paradox prob people =
</syntaxhighlight>
<syntaxhighlight lang = OCaml highlight="2,3">
answer = 23
- : unit = ()
</syntaxhighlight>
合计整数列表.
列表是OCaml中的基础数据类型之一。下面的代码例子定义递归函数codice_16，它接受一个实际参数codice_17，而它被假定为整数的列表。注意关键字codice_18指示了这个函数是递归的。这个函数递归的在给定整数列表之上进行迭代，并提供这些元素的一个总和。codice_19语句类似于C的codice_20语句，但要更加一般性。
<syntaxhighlight lang="ocaml">
let rec sum integers = (* 关键字rec含义为递归。 *)
</syntaxhighlight>
<syntaxhighlight lang="OCaml" highlight="2">
- : int = 15
</syntaxhighlight>
另一种方式是对列表使用标准的fold高阶函数:
<syntaxhighlight lang="ocaml">
let sum integers =
</syntaxhighlight>
<syntaxhighlight lang="OCaml" highlight="2">
- : int = 15
</syntaxhighlight>
因为匿名函数是简单的codice_21算符应用，它可以简写为：
<syntaxhighlight lang="ocaml">
let sum integers =
</syntaxhighlight>
进一步的，还可以通过采用省略列表实际参数：
<syntaxhighlight lang="OCaml">
let sum =
</syntaxhighlight>
快速排序.
OCaml自身可提供对递归算法的简介表达。下列代码例子实现了类似于以升序排序一个列表的quicksort的一个算法：
<syntaxhighlight lang="OCaml">
</syntaxhighlight>
高阶函数.
函数可以接受函数作为参数并且返回函数作为结果。例如，应用codice_22到函数codice_23产生应用codice_23到它的实际参数两次的一个函数：
<syntaxhighlight lang = OCaml>
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);;
</syntaxhighlight>
<syntaxhighlight lang = OCaml highlight="2,4">
- : int = 100
- : string = "Test Test Test Test"
</syntaxhighlight>
函数codice_22使用类型变量codice_26，来指示它可以应用于映射一个类型codice_26到自身的任何codice_23，而非只应用于codice_29函数。特别是，codice_22甚至可以应用于自身：
<syntaxhighlight lang = OCaml highlight="2,4,6">
val fourtimes : ('a -> 'a) -> 'a -> 'a = <fun>
val add4 : int -> int = <fun>
- : int = 102
</syntaxhighlight>
邱奇数.
下列代码定义自然数的邱奇编码，具有后继（succ）和加法（add）。邱奇数codice_15是接受一个函数codice_23和一个值codice_33的一个高阶函数，它应用codice_23到codice_33精确的codice_15次：
<syntaxhighlight lang="OCaml">
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";;
</syntaxhighlight>
为了将一个邱奇数从函数值转换成一个字符串，这里把它传递给向其输入和常量字符串codice_37前置上字符串codice_38的函数codice_39：
<syntaxhighlight lang = OCaml highlight="2">
- : string = "SSSSS0"
</syntaxhighlight>
对象例子.
在OCaml中对象，通过它们的方法的名字和类型，按结构来确定类型。对象可以直接创建（立即对象），而不用通过有名称的类。例如：
<syntaxhighlight lang="ocaml" highlight="7">
val x : < get_x : int; set_x : int -> unit > = <obj>
</syntaxhighlight>
这里OCaml交互式运行时间系统打印出这个对象的推论类型。它的类型codice_40，只由它的方法来定义。换句话说，codice_33的类型由方法类型codice_42和codice_43而非任何名字来定义。类只充当建立对象的函数，例如上例中的对象可以用类来定义，并接着用codice_44算符来建立：
<syntaxhighlight lang="ocaml">
</syntaxhighlight>
要定义有相同的方法和方法类型的另一个对象：
<syntaxhighlight lang="ocaml" highlight="6">
val y : < get_x : int; set_x : int -> unit > = <obj>
</syntaxhighlight>
OCaml将它们视为有相同的类型。例如，等式算符被确定类型为只接受有相同类型的两个值：
<syntaxhighlight lang="ocaml" highlight="2">
- : bool = false
</syntaxhighlight>
所有尽管它们有不同的值，却必定是相同类型的，否则连类型检查都不会做完。这展示了类型等价是结构性的。可以定义调用一个方法的函数：
<syntaxhighlight lang="ocaml" highlight="2">
val set_to_10 : < set_x : int -> 'a; .. > -> 'a = <fun>
</syntaxhighlight>
第一个实际参数的推论类型codice_45是值得关注的。codice_46意味着第一个实际参数，可以是有接受一个codice_6作为实际参数的codice_48方法的任何对象。所以它可以用在对象codice_33之上：
<syntaxhighlight lang="ocaml" highlight="2">
- : unit = ()
</syntaxhighlight>
另一个对象可以碰巧有这个方法和方法类型；其他方法是无关紧要的：
<syntaxhighlight lang="ocaml" highlight="6">
val z : < blahblah : float; set_x : int -> unit > = <obj>
</syntaxhighlight>
codice_50函数对它也有效：
<syntaxhighlight lang="ocaml" highlight="2,3">
10
- : unit = ()
</syntaxhighlight>
这展示了对于事物比如方法调用的兼容性是由结构来确定的。下面为只有一个codice_51方法而没有其他方法的对象定义一个类型同义词（synonym）：
<syntaxhighlight lang="ocaml" highlight="2">
type simpler_obj = < get_x : int >
</syntaxhighlight>
对象codice_33不是这个类型的；但在结构上codice_33是这个类型的一个子类型，因为codice_33包含它的方法的一个超集。所以codice_33可以强制（coerce）成这个类型：
<syntaxhighlight lang="ocaml" highlight="2,4">
- : simpler_obj = <obj>
- : int = 10
</syntaxhighlight>
但是对象codice_56不行，因为它不是一个结构子类型：
<syntaxhighlight lang="console" highlight="2-4">
Error: Type < blahblah : float; set_x : int -> unit > is not a subtype of
</syntaxhighlight>
这展示了拓宽强制的兼容性是结构性的。
任意精度阶乘函数.
可以从OCaml访问各种各样的库。比如，OCaml有内建的任意精度算术。由于阶乘函数增长得非常迅速，会很快溢出机器精度的整数。因此阶乘很适合选用任意精度算术。
在OCaml中，codice_57模块（现在被codice_58所取代）提供了任意精度算术，比如在Ubuntu中安装它：codice_59，它可以如下这样装载到运行中的顶层中：
<syntaxhighlight lang=ocaml>
open Num;;
</syntaxhighlight>
阶乘函数可以使用任意精度算符codice_60、codice_61和codice_62写为：
<syntaxhighlight lang="OCaml">
let rec fact n =
</syntaxhighlight>
这个函数可以计算非常大的阶乘比如codice_63：
<syntaxhighlight lang="OCaml" highlight="2,3">
- : string =
"6689502913449127057588118054090372586752746333138029810295671352301633557244962989366874165271984981308157637893214090552534408589408121859898481114389650005964960521256960000000000000000000000000000"
</syntaxhighlight>
绘制图形例子.
下列程序codice_64使用OpenGL呈现一个缓慢旋转的2D三角形:
<syntaxhighlight lang="OCaml">
let () =
</syntaxhighlight>
需要事先安装负责绑定到OpenGL的LablGL，比如在Ubuntu中安装它：codice_65，这个程序可以如下这样编译成字节码：
<syntaxhighlight lang="console">
$ ocamlc -I +lablGL lablglut.cma lablgl.cma simple.ml -o simple
</syntaxhighlight>
或编译成本机代码：
<syntaxhighlight lang="console">
$ ocamlopt -I +lablGL lablglut.cmxa lablgl.cmxa simple.ml -o simple
</syntaxhighlight>
或者简单的使用codice_66建造命令：
<syntaxhighlight lang="console">
$ ocamlfind opt simple.ml -package lablgl.glut -linkpkg -o simple
</syntaxhighlight>
然后运行：
<syntaxhighlight lang="console">
$ ./simple
</syntaxhighlight>
可以使用OCaml开发非常复杂、高性能的2D和3D图形程序。使用OpenGL和OCaml，结果的程序可以跨平台编译而在主要平台上无需改动。