Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.Sign up
Surprising interaction between parenthesizing applications and default arguments #7832
Original bug ID: 7832
A bug in ocamlformat found by hhugo uncovered some, to me, surprising sensitivity of the type checker to the presence of parentheses around applications. See the following toplevel interaction for example:# let id x = x;; val id : 'a -> 'a = # let plus a ?(b=0) c = a + b + c;; val plus : int -> ?b:int -> int -> int = # id (plus 1);; - : ?b:int -> int -> int = # id (plus 1) 1;; - : int = 2 # (id (plus 1)) ~b:1;; - : int -> int = # id (plus 1) ~b:1;; Error: This expression has type ?b:int -> int -> int but an expression was expected of type b:'a -> 'b
Note the last two expressions are the same except for two nested 1-argument applications versus a single 2-argument application, but the first types and the second doesn't. Is this difference expected? Is there a description of when such distinctions are important? (Ideally ocamlformat would emit the parens where needed but not have to for all nested applications, so I'm looking for some syntactic criterion to use to distinguish which Parsetrees need the parens and which don't.)
Comment author: @garrigue
There is a specification of the dynamic semantics in "Labeled and optional arguments for Objective Caml" (http://www.math.nagoya-u.ac.jp/~garrigue/papers/index.html). Yhe problem you describe here is with the type checking, but the two are related.
For type checking, the rule is as follows:
So the point is that when typing an application without parentheses, unifications are delayed to the end.
So the answer is that you should be keeping the parentheses as they were in the original source code, since they may change both the static and dynamic behavior.