OCaml already allows you to declare a variable inside a (tuple or variant) argument of a function to be unused in the rest of the function body, by prefixing the name with a _.
As far as I see, such a feature could also be implemented for values returned by
a function ; inside a returned uple or a variant, one could also have a special convention
for saying to the compiler “This value here will never be used, put anything you like as long
as it has the correct type”. This presupposes that the compiler knows how to construct a default
value of any given type.
An extra luxury would be to have a warning issued when the declared unused term is in fact used
later in the code.
This is undecidable for GADTs and impossible for abstract types. In general, I would say it is far better to not construct such never-used value both conceptually and for readability purpose.
I’d be interested to hear the concrete use cases you have in mind for this kind particular functionality.
It is tangential to what you are asking for explicitly, but statically tracking resource usage and data flow is one of the primary focal points of Jane Street’s oxcaml project. These kinds of properties are expressed via the mode system.
AFAIK, it will not let you express
This value here will never be used, put anything you like as long
as it has the correct type
(I don’t know why you’d want to express this). But you can express things like “this value will never escape the body of this function” (locality) or “this value can only be used once” (linearity) or “this value can only ever be the referent of a single reference” (uniqueness).
So far I have exactly one, actually : it’s when a function returns an uple (or a variant) and in some
case some components of that returned uple or variant are never to be used in your general computation,
so you can put anything there as it has the correct type - in that kind of situation I feel it’s cleaner
to have an adhoc syntax rather than putting a random value.
For a more detailed example, see “Choice 2” in my other question here, at Optimal way of writing a debugging-friendly recursion?
On the face of it, it sounds to me like the functions are incorrect then. Why are they returning values that should never be used? We have plenty of mechanisms at the type level to make it impossible to expose data that should remain private. Glancing at your Choice 2, it’s not clear to me how it relates: which part is returned never to be used?
I don’t know if I follow, but you can give part of the tuple an abstract type.
module Open = struct
type work = int
let f () = 1, 2
end
module Closed : sig
type work
val f : unit -> int * work
end =
Open
which you can do inline with the rest of your current module by immediately opening these:
module M = struct
type work = int
let f () = 1, 2
end
open M
let show_f () =
let r, w = f () in
Printf.printf "result=%d, work=%d\n" r w
open (M : sig
type work
val f : unit -> int * work
end)
let show_f () =
let r, _ = f () in
Printf.printf "result=%d, work=** can't show **\n" r
The main difference between the two choices is in the type used. In Choice 1 we create a brand new type
for the occasion, type iterator_state = Work_still_in_progress of type1 |Computation_finished of type2
and in choice 2 we use an (imperfect) native equivalent, (t2 option)*t1. According to which
variant is represented, one element of the pair or the other is a “dummy”.
I agree with that in principle, but I would make exceptions for small bits of code that are deep in the nesting hierarchy. The interface of a module should be very clean, the first layers of code in the implementation should be clean also, but for the deeper layers there begins to appear a compromise between strictly clean code and fast-to-write code. In the example we’re discussing, I personally feel that it is overkill to create a new type just for this small task.
There are some types for which the compiler can generate type-safe generic values that fail if actually used: the ones allowed in safe recursive modules. The types for which it is allowed are fairly restricted (functions and lazy values), but that could be a hint to solve your problem: if a field does not have a value on all paths, wrap it in a Lazy.t and use lazy (assert false) on the paths with no values.
I don’t expect the language to ever support automatic value generation (because it’s hard and because it’s dangerous) but a ppx might be able to provide a best-effort approach for the easy cases..