Ответ 1
Взяв намек на то, что сделает Haskell (объявите класс типа (Sequential a) => Range a
), вы можете использовать функтор:
module Range (S : sig type t end) = struct
type range = Full | Range of (S.t * S.t)
end
и использовать его для предоставления необходимых модулей:
module IntRange = Range (struct type t = int end)
module FloatRange = Range (struct type t = float end)
module CharRange = Range (struct type t = char end)
Недостатком является то, что вы теряете параметричность на range
; что ваши параметрические функции на range
теперь живут внутри модуля range
, как они, вероятно, должны.
В общем случае range
выполнит ряд требований Sequential
, чтобы компенсировать потерю параметричности. Эти требования могут быть четко определены в сигнатуре параметра functor:
module type SEQUENTIAL = sig
type t
val to_string : t -> string
val compare : t -> t -> int
(* ... *)
end
module Range (S : SEQUENTIAL) = struct
type t = Full | Range of (S.t * S.t)
let to_string = function
| Full -> "full"
| Range (lo, hi) -> "(" ^ S.to_string lo ^ "," ^ S.to_string hi ^ ")"
let make lo hi =
if S.compare lo hi > 0 then Range (hi, lo) else Range (lo, hi)
end
Чтобы создать экземпляр range
в определенном типе, вам теперь необходимо создать структуру, которая правильно ее параметризует:
module IntRange = Range (struct
type t = int
let to_string = string_of_int
let compare = Pervasives.compare
end)
Затем вы можете использовать его следующим образом:
# IntRange.(to_string (make 4 2)) ;;
- : string = "(2,4)"
(с использованием нового синтаксиса для ограничения перегрузки). Если вам нужно скрыть реализацию range
за подписью, вам может потребоваться повторно экспортировать тип Sequential
s, как это делают структуры данных в стандартной библиотеке:
module Range (S : SEQUENTIAL) : sig
type elt = S.t
type t = private Full | Range of (elt * elt)
val to_string : t -> string
val make : elt -> elt -> t
end = struct
type elt = S.t
type t = Full | Range of (elt * elt)
let to_string = function
| Full -> "full"
| Range (lo, hi) -> "(" ^ S.to_string lo ^ "," ^ S.to_string hi ^ ")"
let make lo hi =
if S.compare lo hi > 0 then Range (hi, lo) else Range (lo, hi)
end
Это дает вам инкапсуляцию и полупрозрачные типы, которые могут быть сопоставлены с образцом, но не сконструированы. Альтернативой объявлению типов private
в сигнатуре является использование типа представления или функции деструктурирования.