I want to define a type so that all construction goes through module members that can preserve invariants, but allow destructuring for pattern matching.
I'm just learning OCaml but the following almost works for an int pair with the invariant that the left should be strictly less than the right
module Range : sig
type t = private { left:int; right:int }
exception InvalidRange of (int*int)
val make : int -> int -> t
end = struct
type t = { left:int; right:int }
exception InvalidRange of (int*int)
let make left right = if left < right
then { left; right }
else raise (InvalidRange (left, right))
end
which works thus
# let p = Range.make 1 2;;
val p : Range.t = {Range.left = 1; Range.right = 2}
# let q = Range.make 2 1;;
Exception: Range.InvalidRange (2, 1).
and destructuring works after a fashion
# let {Range.left=x; Range.right=y} = p;;
val x : int = 1
val y : int = 2
while constructing fails
# let badp = {Range.left = 2; Range.right = 1};;
let badp = {Range.left = 2; Range.right = 1};;
Error: Cannot create values of the private type Range.t
# open Range;;
# let badp = {left = 2; right=1};;
let badp = {left = 2; right=1};;
Error: Cannot create values of the private type Range.t
but what I would really like to do is have the syntactic convenience of destructuring tuples. The below does not work:
module Range : sig
type t = private int*int
exception InvalidRange of (int*int)
val make : int -> int -> t
end = struct
type t = int*int
exception InvalidRange of (int*int)
let make left right = if left < right
then (left, right)
else raise (InvalidRange (left, right))
end
but then I can't destructure it using a tuple pattern:
# let r = Range.make 1 2 ;;
val r : Range.t = (1, 2)
# let (a, b) = r;;
let (a, b) = r;;
Error: This expression has type Range.t
but an expression was expected of type 'a * 'b
I could change the type to type t = R of (int * int)
but I need these to be as light-weight memory-wise as possible. Any ideas?
As explained in the manual, you need an explicit coercion:
# let (a, b) = (r :> int*int);;
val a : int = 1
val b : int = 2