Constraining a polymorphic type
I've got a range type defined as:
type 'a range = Full | Range of ('a * 'a)
However, I'd like to constrain 'a to be integer or float or char, with no other valid types for '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 *)
I figured that I could change my type definitions to:
type sequential 开发者_如何转开发 = S_int of int | S_float of float | S_char of char ;;
type range = Full | Range of (sequential * sequential);;
However, this would then allow something like:
Range(S_int(0), S_float(10.0));; (* problem: mixes int and float *)
...but I want both components of Range to be the same type.
I suppose that another approach would be to create an int_range type, a float_range type, and a char_range type but I'm wondering if there's another way?
Another approach is to declare type private and expose functions constructing it only with the types you want, e.g. :
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
Taking a hint from what Haskell would do (declare a type class (Sequential a) => Range a
) you could use a functor:
module Range (S : sig type t end) = struct
type range = Full | Range of (S.t * S.t)
end
and use it to provide the required modules:
module IntRange = Range (struct type t = int end)
module FloatRange = Range (struct type t = float end)
module CharRange = Range (struct type t = char end)
The downside is that you lose parametricity on range
; the upside is that your parametric functions on range
s now live inside the module Range
, as they probably should.
In general, Range
s will make a number of demands of Sequential
s in order to compensate for the loss of parametricity. These requirements can be cleanly specified in the signature of the functor parameter:
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
To instantiate the Range
at a specific type you now need to provide a structure that properly parameterizes it:
module IntRange = Range (struct
type t = int
let to_string = string_of_int
let compare = Pervasives.compare
end)
Then you can use it like this:
# IntRange.(to_string (make 4 2)) ;;
- : string = "(2,4)"
(using the new syntax for delimited overloading). If you need to hide the implementation of Range
s behind a signature, you might need to re-export the type of SEQUENTIAL
s, much as the data structures in the standard library do:
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
This gives you encapsulation and translucent types that can be pattern-matched but not constructed. An alternative to declaring private
types in the signature is to use a view type or a destructuring function.
OMG modules are so complicated!
type 'a range' = [`Full | `Range of 'a * 'a]
type range = [
| `Int_range of int range'
| `Float_range of float range'
]
Oh dang, we need to add another one:
type xrange = [
| range
| `String_range of string range'
]
精彩评论