types functional-programming ocaml

types - Restringir un tipo polimórfico



functional-programming ocaml (3)

Tengo un tipo de rango definido como:

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

Sin embargo, me gustaría restringir ''a ser entero o flotar o char, sin otros tipos válidos para'' 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 *)

Pensé que podría cambiar mis definiciones de tipo a:

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

Sin embargo, esto permitiría algo así como:

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

... pero quiero que ambos componentes de Range sean del mismo tipo.

Supongo que otro enfoque sería crear un tipo int_range, un tipo float_range y un tipo char_range pero me pregunto si hay otra forma.


Otro enfoque es declarar el tipo privado y exponer las funciones construyéndolo solo con los tipos que desee, por ejemplo:

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


Tomando una pista de lo que haría Haskell (declare una clase de tipo (Sequential a) => Range a ) podría usar un functor:

module Range (S : sig type t end) = struct type range = Full | Range of (S.t * S.t) end

y úsala para proporcionar los módulos requeridos:

module IntRange = Range (struct type t = int end) module FloatRange = Range (struct type t = float end) module CharRange = Range (struct type t = char end)

La desventaja es que se pierde la parametricidad en el range ; Lo bueno es que sus funciones paramétricas en el range s ahora viven dentro del Range del módulo, como probablemente deberían.

En general, Range s hará una serie de demandas de s Sequential para compensar la pérdida de parametricidad. Estos requisitos pueden ser claramente especificados en la firma del parámetro del 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

Para instanciar el Range en un tipo específico, ahora necesita proporcionar una estructura que lo parametrice correctamente:

module IntRange = Range (struct type t = int let to_string = string_of_int let compare = Pervasives.compare end)

Entonces puedes usarlo así:

# IntRange.(to_string (make 4 2)) ;; - : string = "(2,4)"

(usando la nueva sintaxis para la sobrecarga delimitada). Si necesita ocultar la implementación de Range s detrás de una firma, es posible que deba volver a exportar el tipo de SEQUENTIAL s, de forma muy similar a como lo hacen las estructuras de datos en la biblioteca estándar:

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

Esto le proporciona encapsulación y tipos translúcidos que pueden combinarse con patrones pero no construirse. Una alternativa para declarar tipos private en la firma es usar un tipo de vista o una función de desestructuración.


¡Los módulos de OMG son tan complicados!

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

Oh dang, tenemos que agregar otro:

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