Получив подсказку о том, что будет делать 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
s теперь живут внутри модуля Range
, как они, вероятно, должны.
В общем случае Range
s будет предъявлять ряд требований Sequential
sчтобы компенсировать потерю параметричности.Эти требования могут быть четко указаны в сигнатуре параметра 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
s за подписью, вам может потребоваться реэкспорт типа 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
типов в сигнатуре является использование типа представления или функции деструктуризации.