Ограничивающий полиморфный тип - PullRequest
12 голосов
/ 03 июня 2011

У меня есть тип диапазона, определенный как:

type 'a range = Full | Range of ('a * 'a)

Тем не менее, я бы хотел ограничить a целочисленными значениями, числами с плавающей запятой или символами без других допустимых типов для a.

Range(0,10) (* valid *)
Range(0.0, 10.0) (* valid *)
Range('a', 'z') (* valid *)
Range("string1", "string2") (* other types like this shouldn't type check *)

Я подумал, что могу изменить определения типа на:

type sequential   = S_int of int | S_float of float | S_char of char ;;
type range = Full | Range of (sequential * sequential);;

Однако, тогда это позволило бы что-то вроде:

Range(S_int(0), S_float(10.0));; (* problem: mixes int and float *)

... но я хочу, чтобы оба компонента Range были одного типа.

Я полагаю, что другим подходом было бы создание типа int_range, типа float_range и типа char_range, но мне интересно, есть ли другой способ?

Ответы [ 3 ]

12 голосов
/ 03 июня 2011

Другой подход - объявить тип private и предоставить функции, создавая его только с теми типами, которые вам нужны, например, :

module Z : sig
  type 'a range = private Full | Range of ('a * 'a)
  val int_range : int -> int -> int range
  val float_range : float -> float -> float range
  val string_range : string -> string -> string range
  val full : 'a range
end = struct
  type 'a range = Full | Range of ('a * 'a)
  let create x y = Range (x,y)
  let int_range = create
  let float_range = create
  let string_range = create
  let full = Full
end

# open Z;;
# int_range 2 3;;
- : int Z.range = Range (2, 3)
# Range ('a','c');;
Error: Cannot create values of the private type char Z.range
10 голосов
/ 03 июня 2011

Получив подсказку о том, что будет делать 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 типов в сигнатуре является использование типа представления или функции деструктуризации.

0 голосов
/ 26 февраля 2012

Модули OMG настолько сложны!

type 'a range' = [`Full | `Range of 'a * 'a]
type range = [
  | `Int_range of int range'
  | `Float_range of float range'
]

О черт, нам нужно добавить еще один:

type xrange = [
  | range
  | `String_range of string range'
]
...