OCamlなどの一部の関数型言語には、抽象データ型を実装するメカニズムが組み込まれているため、一部の不変条件が適用されます。このようなメカニズムを持たない言語は、不変条件を強制するためにユーザーが「カーペットの下を見ない」ことに依存しています。
OCamlの抽象データ型
OCamlでは、モジュールはプログラムを構成するために使用されます。モジュールには実装とシグネチャがあり、後者はモジュールで定義されている値と型の一種の要約ですが、前者は実際の定義を提供します。これは.c/.h
、Cプログラマーがよく知っている2部作と大まかに比較できます。
例として、次のようにFraction
モジュールを実装できます。
# module Fraction = struct
type t = Fraction of int * int
let rec gcd a b =
match a mod b with
| 0 -> b
| r -> gcd b r
let make a b =
if b = 0 then
invalid_arg "Fraction.make"
else let d = gcd (abs a) (abs b) in
Fraction(a/d, b/d)
let to_string (Fraction(a,b)) =
Printf.sprintf "Fraction(%d,%d)" a b
let add (Fraction(a1,b1)) (Fraction(a2,b2)) =
make (a1*b2 + a2*b1) (b1*b2)
let mult (Fraction(a1,b1)) (Fraction(a2,b2)) =
make (a1*a2) (b1*b2)
end;;
module Fraction :
sig
type t = Fraction of int * int
val gcd : int -> int -> int
val make : int -> int -> t
val to_string : t -> string
val add : t -> t -> t
val mult : t -> t -> t
end
この定義は次のように使用できます。
# Fraction.add (Fraction.make 8 6) (Fraction.make 14 21);;
- : Fraction.t = Fraction.Fraction (2, 1)
誰でも、組み込みのセーフティネットをバイパスして、fraction型の値を直接生成できますFraction.make
。
# Fraction.Fraction(0,0);;
- : Fraction.t = Fraction.Fraction (0, 0)
これを防ぐために、そのFraction.t
ような型の具体的な定義を隠すことができます:
# module AbstractFraction : sig
type t
val make : int -> int -> t
val to_string : t -> string
val add : t -> t -> t
val mult : t -> t -> t
end = Fraction;;
module AbstractFraction :
sig
type t
val make : int -> int -> t
val to_string : t -> string
val add : t -> t -> t
val mult : t -> t -> t
end
を作成する唯一の方法AbstractFraction.t
は、AbstractFraction.make
関数を使用することです。
Schemeの抽象データ型
Scheme言語には、OCamlと同じ抽象データ型のメカニズムはありません。カプセル化は、「カーペットの下を見ない」ユーザーに依存しています。
Schemeではfraction?
、入力を検証する機会を与える値の認識などの述語を定義するのが慣例です。私の経験では、主な使用法は、ユーザーが値を偽造する場合、各ライブラリー呼び出しで入力を検証するのではなく、ユーザーが入力を検証できるようにすることです。
ただし、適用されたときに値を生成するクロージャーを返す、またはライブラリーによって管理されるプール内の値への参照を返すなど、戻り値の抽象化を強制するいくつかの戦略がありますが、実際にそれらを見たことはありません。