Type-bound value constructors with checks

When I type:

type even = int

I am creating a module-level alias for the int type, right? How can I guarantee evenness of values with this kinda wrapper type that should add more constraints on the value? Obviously, the value constructor should be responsible for such a check. Can I define a constructor function even () and guarantee that all values of type even are created using the even () constructor that performs a check for evenness? What is OCaml’s idiomatic approach to do stuff like that?

Note 1: Of course, even: int -> even is not possible or requires exceptions that I’m trying to avoid at any cost, so that and the return value should be of type even option, but it is not important detail here.

Note 2: I have no real problem that I’m solving. I just want to discuss the approach of how to work with such value restrictions in OCaml on type/meta level.

1 Like

The simplest approach is simply to make even abstract. That way you can control exactly how values of that type are constructed. For example, if you define

module Even : sig
  type even
  val even : int -> even option
  val to_int : even -> int
end = struct
  type even = int
  let even n = if n mod 2 = 0 then Some n else None
  let to_int n = n
end

then the only way to construct values of type even is to go via the Even.even function.

Cheers,
Nicolás

3 Likes

In addition to what @nojb explains, I think it is a case where you may like to declare a private type abbreviation in the signature:

type even = private int

so that you can write (x :> int) instead of Even.to_int x. This is very close to the example given in section 3.2 Private type abbrevations of the manual.

4 Likes
module Even : sig
  type t = private int
  val make : int -> t
end = struct
  type t = int
  let make n = n + n
end