ocaml/ocaml — pull request #76 by Drup (2014-06-26 23:09:32+00:00)
Title: Infix constructors

It was previously possible to overwrite the constructor `::`. This patch allows infix constructors like this :
```ocaml
# type arit = ( :+ ) of arit * arit | ( :* ) of arit * arit | Int of int;;
type arit = ( :+ ) of arit * arit | ( :* ) of arit * arit | Int of int

(* priorities and associativities are as expected *)
# let affine x a b = Int a :+ Int b :* x ;;
val affine : arit -> int -> int -> arit =

(** The various printers have been adapted *)
# affine (Int 1) 3 2 ;;
- : arit = ( :+ ) (Int 3, ( :* ) (Int 2, Int 1))

(** It works in patterns with the right priorities too *)
# let factorize = function
| a :* x :+ b :* y when a = b -> a :* (x :+ y)
| arit -> arit
in factorize ((Int 3 :* Int 4) :+ (Int 3 :* Int 5)) ;;
- : arit = ( :* ) (Int 3, ( :+ ) (Int 4, Int 5))
```

The design is based on the one used by infix operators:
- An infix constructor must start by `:`, analogous to the fact that a constructor must start by a capital letter.
- Priorities and associativities are the same as operators, as specified by [this table](http://caml.inria.fr/pub/docs/manual-ocaml/expr.html) but with regards to the **second** character. `:+` will behave as `+`, and so will the very pretty `:+<>`.
- Infix constructors starting by a `::` (`::@` for example) will have the same priority and associativity as `::`, which is unchanged.
- the prefix characters `!`, `?` and `~` can't be used inside an infix operators for the sake of retro-compatibility. The construction `a::!r` was parsed `a :: !r` and this proposal keep this behavior.

There are three exceptions to the above rules: `:>`, `:=` and `:<` **can't** be used as infix constructors (but they **can** be prefix, `:><:` is a valid infix constructor). The reason for the first two are obvious, the last one is forbidden because of the following piece of code: `x:`.

To allow easier usage of the infix constructors and for consistency, I modified the parser so that
- An infix constructor must be declared surrounded by parenthesis, as an operator.
- An infix operator can be used as a normal constructor, surrounded by parenthesis : `(:+) (Int 3, Int 4)`.
- Every constructor can be prefixed by a module to address the constructor from this module. : `M.Cons` (like before), `M.(:+)`, `M.[]`, `M.()`, `M.true` and `M.false`. All of them are rebindable (yes, including the last four. It was already the case before except for `[]`, and I changed it for consistency).

Implementation notes:
- https://github.com/Drup/ocaml/commit/e34dc324e9a858ba0d09dc4b916feaf485b0b66d implements a simpler proposal, with only infix constructors of the form `::...`.
- https://github.com/Drup/ocaml/commit/1ba59cf2c085d5ef317c531ea9d1b908f4727c0c implements the infix constructor of the form `:..` with various priorities and associativities.
- https://github.com/Drup/ocaml/commit/c0cf9af1a226542901fc93326a26f33cc3ffcdf3 modifies the parser to allow the simplification explained in the previous paragraph.
- https://github.com/Drup/ocaml/commit/1ba59cf2c085d5ef317c531ea9d1b908f4727c0c fixes the pretty printers. I tried to be thorough, but it's quite possible I missed some). I tried to make my modifications as simple as possible (no priority guessing to reduce the amount of parenthesis, for example). I was tempted to share some code between the various printers, but the build system disagreed with me on that, and I didn't dare to argue with him.
- https://github.com/Drup/ocaml/commit/52d52fe41f7362dbef1100b563c374ce8927a0f1 fixes a lexer in ocamldoc/ ... I'm not actually sure what it's for (rendering of code between [] in ocamldoc comments, maybe ?), but I preferred to keep it in sync with the main lexer nevertheless.

The compiler bootstrap and the testsuite passes.

TODO:
- Check that I didn't miss any corner cases (opam and CI, I believe in you!)
- Maybe make the toplevel printer nicer, so that `(:+) (Int 3, Int 4)` is printed `Int 3 :+ Int4` ... but I'm not actually sure it's a good idea, since I don't know how to print `M.(:+) (Int 3, Int 4)`, `M.(Int 3 :+ Int4)` might not be correct.
- Add tests ? I couldn't find infix associativities and priorities tests, where are they ?

Another example, from [this](http://cedeela.fr/batch-operations-on-collections.html) :
```ocaml
type (_, _) op =
Id : ('a, 'a) op
| ( ::@ ) : ('a, 'b) base_op * ('b, 'c) op -> ('a, 'c) op
...
let rec _optimize_head
: type a b. (a,b) op -> (a,b) op optim_result
= fun op -> match op with
| Map f ::@ Map g ::@ cont -> ...
...
```