Consider this interactive session:
utop # type a = A of int * int;;
type a = A of int * int
utop # type b = B of (int * int);;
type b = B of (int * int)
utop # A (3,4);;
a = A (3, 4)
utop # A ((3,4));;
a = A (3, 4)
utop # let t = (3,4);;
val t : int * int = (3, 4)
utop # A t;;
Error: The constructor A expects 2 argument(s),
but is applied here to 1 argument(s)
utop # B (3,4);;
b = B (3, 4)
utop # B t;;
b = B (3, 4)
There is nothing wrong here: A has two arguments, B one. Still I find it ugly that values of type a are printed in exactly the same way as values of type b. (The types themselves are distinguishable when printed).
Would it make sense to print variants whose only argument is a literal tuple like this: B ((3,4)) ?
Also, A ((3,4)) is parsed as A (3,4) and is accepted, although the first form to me intuitively looks like a single argument to the constructor. Would it be cleaner to reject A ((3,4))?
Finally, if backwards compatibility were not an issue, would the language not work just as well if only constructors with one (possibly tuple) argument were allowed at all?