14.6 ファンクタのサポート
SML#の分割コンパイルシステムは,ファンクタもオブジェク トファイルに分割コンパイルし,他のコンパイル単位から_require宣言 を通じて利用することができます. ファンクタのインターフェイスファイルは,そのProvide宣言に以下のよ うに記述します.
functor () =
struct
(* この部分はstructureのProvideと同一 *)
end
ここで,はStandard ML構文規則に従うシグネチャ宣言です. インターフェイスと似ていますが,インターフェイスではなく,通常のシグ ネチャが書けます. 以下に二分探索木を実現するファンクタのインターフェイ スファイルの例をしめします.
_require "basis.smi"
functor BalancedBinaryTree
(A:sig
type key
val comp : key * key -> order
end
) =
struct
type ’a binaryTree (= boxed)
val empty : ’a binaryTree
val isEmpty : ’a binaryTree -> bool
val singleton : key * ’a -> ’a binaryTree
val insert : ’a binaryTree * A.key * ’a -> ’a binaryTree
val delete : ’a binaryTree * key -> ’a binaryTree
val find : ’a binaryTree * A.key -> ’a option
end
ただし,この機構を利用するプログラマは,以下の点に留意する必要が あります.
-
•
functorはモジュール分割のための道具ではない. 分割コンパイルができないML系言語処理系では,モジュールの間の直接 の依存関係を断ち切る手段としてのファンクタの使用が推奨されることがありました. 例えば,
A.smlファイル: structure A =
struct
...
end B.smlファイル: structure B =
struct
open A
...
endと書くとB.smlファイルが別な実装ファイルA.smlファイルに直接依 存してしまいます. このB.smlをファンクタを使い以下のように書きなおせば 依存性は解消されます.
B.smlファイル: functor B(A:sig ... end) =
struct
open A
...
endこの機能は,分割コンパイルの機能そのものです. 分割コンパイルとリンクの機能を完全にサポートしているSML# では,この目的のためにファンクタを使う必要はなく,このような使用は 避けるべきです.
-
•
ファンクタの利用にはコストがかかる. ファンクタは,関数などの値以外に型もパラメタとして受け取る能力があ ります. これが,ファンクタなしでは達成できないファンクタ本来の機能です. しかし,同時に型をパラメタとして受け取り,その型に応じた処理を行 うため,ファンクタ本体のコンパイルには,型が決まっているストラクチャに比べて コンパイルにもコンパイルされたオブジェクトコードにもオーバヘッドが生まれ ます. これは,高度な機能を使用する上で避けられないことです. ファンクタは,このオーバヘッドを意識して,コードすべき高度な機能です.
現在のSML#のファンクタの実装には以下の制限があります.
-
•
ファンクタの引数に型引数を持つ抽象型コンストラクタが含まれている 場合,その型コンストラクタにはヒープにアロケートされる実装表現 (array, boxed, {}, ->, *など) を持つ型のみを適用する ことができます. 例えば,SML#では以下の例はコンパイルエラーになります.
# functor F(type ’a t) = struct end structure X = F(type ’a t = int);
(interactive):2.17-2.34 Error:
(name evaluation "440") Functor parameter restriction: t