You can omit labels if the application is total (i.e., all required arguments are provided) and if the return type of the function is not a type variable. Arguments to the optional parameters must always be passed by a label.
Let's do some examples,
let example1 ?(opt=0) ~a ~b ~c unlabeled =
opt + a + b + c + unlabeled;;
example1 1 2 3 4;;
- : int = 10
Here we were able to apply all arguments without labels, because we have provided all required arguments (the optional parameter is not required, hence the name), and the resulting type is not polymorphic. However, if we will take the List.fold function from Core or ListLabels, which has type
'a list -> init:'accum -> f:('accum -> 'a -> 'accum) -> 'accum
Then we will get,
List.fold [1;2;3;4] 0 (+);;
- : init:(int -> (int -> int -> int) -> '_weak1) ->
f:((int -> (int -> int -> int) -> '_weak1) ->
int -> int -> (int -> int -> int) -> '_weak1) ->
'_weak1
instead of 10, which one would expect. The reason for that is since the resulting type 'accum is a type variable, so it could also be a function, e.g., int -> int or string -> int -> unit, etc -- all those types match with the 'accum type. That basically means that this function accepts a potentially infinite number of positional arguments. Therefore, all arguments that we provided were interpreted as positional, and as a result, we never were able to fill in the labeled arguments, therefore, our application wasn't made total. This actually makes our original definition of the rule at the beginning of the posting redundant - since an application, which type is denoted with a type variable, could never be total.
Note, that this problem only occurs when the return type is a type variable, not just includes some type variables, for example, we can easily omit labels with the List.map function, e.g, given List.map from Core, which has type
'a list -> f:('a -> 'b) -> 'b list
we can easily apply it
# List.map [1;2;3] ident;;
- : int Core_kernel.List.t = [1; 2; 3]
With all that being said, it is usually assumed a bad practice to omit the labels. Mainly because of the caveats with polymorphic return types and because it makes the order of the labeled arguments important, which is sort of counter-intuitive.