Abusing list notations and GADTs to keep track of length and ensure exhaustive matching:

module M = struct type ('a, 'b) t = [] : (_, unit) t | (::) : 'a * ('a, 'b) t -> ('a, unit * 'b) t end;;
let rec to_list : type a b. (a, b) M.t -> a list = function | M.[] -> [] | M.(x :: xs) -> x :: to_list xs;;
let decompose x = (to_list x, x);;

# let all_items, M.[item1; item2] = decompose M.["hello"; "world"];;
val all_items : string list = ["hello"; "world"]
val item1 : string = "hello"
val item2 : string = "world"

On Fri, Aug 9, 2019 at 4:27 PM Richard W.M. Jones <rich@annexia.org> wrote:
Let's imagine you have a list of named things, but you also want to
collect them up into a single list.  For example:

  let item1 = "hello"
  let item2 = "world"
  let all_items = [ item1; item2 ]

  let () =
    printf "item1 = %s\n" item1;
    printf "all_items = %s\n" (String.concat ", " all_items)

This is fine, but there's a danger that a programmer could add item3
but forget to add it to the "all_items" list.  (In the real world
problem to which this applies, my items are complex and lengthy
structures, and the "all-things-list" is well off the bottom of the
page when you're viewing the top item).

My idea to fix this was to write:

  let all_items = [
    ("hello" as item1);
    ("world" as item2);
  ]

Actually I was slightly surprised to find this doesn't compile.  I
guess because "as" can only be used like this in patterns, not
expressions.  Also the scoping is wrong because the "as item1" if it
worked probably wouldn't apply outside the list.

I haven't worked out if it's possible to write this in ordinary OCaml,
although I suppose ppx could solve it.  Any ideas if this is possible?

Rich.