これは、SML のファンクターで解決できる種類の問題だと思います。
たとえば、質問で関数を定義する TOTALORDER という署名の存在を考えてみましょう (lt はより低いことを意味します)。
signature TOTALORDER =
sig
type element
val lt: element * element -> bool
end
ご覧のとおり、関数は として定義されておりelement * element -> bool
、要素の型はここでは定義されていません。
次に、次のように、TOTALORDER の 2 つの異なる実装を定義して、異なる型で動作するようにします。
structure String : TOTALORDER =
struct
type element = string
fun lt(a:string, b) = a < b
end
structure Integer: TOTALORDER =
struct
type element = int
fun lt(a, b) = a < b
end
上記で、文字列を操作できる実装と、整数を操作できる別の実装を定義しました。これらの実装では、 の実際の型を定義していることがわかりますelement
。
これで、次のように、ファンクターで交換可能な型の魔法を定義できます。
functor MakeComparator(Lt: TOTALORDER):
sig
val descending : Lt.element * Lt.element -> Lt.element * Lt.element
val ascending : Lt.element * Lt.element -> Lt.element * Lt.element
end
=
struct
open Lt;
fun descending(a,b) = if lt(a,b) then (b,a) else (a,b)
fun ascending(a,b) = if lt(a,b) then (a,b) else (b,a)
end
ここで、TOTALORDER 定義に基づいて、ファンクターが、昇順と降順という 2 つの関数でシグネチャを定義していることがわかります。ファンクターは、そのような署名の実装をパラメーターとして受け取ります。その後、構造体の実装でそれを使用して、ペアを昇順または降順でソートします。
したがって、最終的には、a と b の型は、ファンクターに提供される TOTALORDER の実装における要素の型に依存します。
次のように、さまざまな比較タイプを使用してさまざまな実装を作成できるようになりました。
structure StringComparator = MakeComparator(String)
structure IntegerComparator = MakeComparator(Integer)
そして、それらの型に応じてそれらを使用できます。例えば:
val res01 = StringComparator.ascending("arm","house") (*(arm,house)*)
val res02 = StringComparator.descending("arm","house") (*(house,arm)*)
val res03 = IntegerComparator.ascending(1,2) (*(1,2)*)
val res04 = IntegerComparator.descending(1,2) (*(2,1)*)
型クラスを持つ Haskell のような他の言語と比較すると、確かに冗長ですが、問題を解決するための有効なアプローチだと思います。