Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uniformity of handling of operators #657

Open
Niols opened this issue Dec 1, 2023 · 4 comments
Open

Uniformity of handling of operators #657

Niols opened this issue Dec 1, 2023 · 4 comments
Labels

Comments

@Niols
Copy link
Member

Niols commented Dec 1, 2023

Is your feature request related to a problem? Please describe.

I would like operators in OCaml to be handled in a consistent and predictable way. For now, this does not seem to be the case. I don't think the way they should be handled has been clearly defined, though, so maybe we should have a first discussion about this. As an example, here are how a bunch of a <op> b <op> c multiline expressions are handled by Topiary:

a#b#c
;;

a * b *
  c
;;

a = b
= c
;;

a **
  b **
    c
;;

a :: b :: c
;;

a @
b @
c

(Note that some of them are not even output in a multi-line way; cf #658 for the specific case of ::.)

Describe the solution you'd like

I would like there to be clear, simple and syntactic rules on how to format operators. Precedence and priorities are known statically in OCaml so I think we could come up with something pretty good with that. I believe in indentation to show that kind of expressions. For the example above, I would go for:

Left associative:

a
  # b
  # c
;;

a
  * b
  * c
;;

a
  = b
  = c
;;

Right associative on the right, maybe? Although for :: and @ that just looks weird, but I think I'd get used to that pretty easily:

a **
  b **
  c
;;

a ::
  b ::
  c
;;

a @
  b @
  c

Or otherwise on the left if we believe that makes more sense (but it would be equally disturbing for things such as @@):

a
  ** b
  ** c
;;

a
  :: b
  :: c
;;

a
  @ b
  @ c

Additional context

Related to #261 and #259 (comment).

Here is a tiny script to generate and call Topiary on various OCaml operators, for quick comparison:

## For an operator `op`, output a sequence `a op b op c` in multiline to
## explicit Topiary's behaviour. For instance, for +, produce:
##
##     let () = a + b +
##              c
##     ;;

g () {
    for op; do
      echo "
        let () = a $op b $op
                 c
      "
    done
}

## Based on table from OCaml manual, section 7.1 Precedence and associativity.
## https://v2.ocaml.org/manual/expr.html#ss%3Aprecedence-and-associativity

{
    echo '(* Left associative *)'

    g '#' '##'
    g '*' '*+' '*.' '/' '/.' '%' 'mod' 'land' 'lor' 'lxor'
    g '+' '+.' '-' '-.'
    g '=' '==' '=>' '<' '<$>' '>' '>>=' '|=' '&=' '$' '!='

    echo '(* Right associative *)'

    g '**' 'lsl' 'lsr' 'asr'
    g '::'
    g '@' '@@' '^' '^='
    g '&' '&&'
    g 'or' '||'
    g '<-' ':='
    g ';'

} | topiary -- format -l ocaml

Here is the output as of 8cc9aa4:

(* Left associative *)

let () =
  a#b#c

let () =
  a##b##c

let () =
  a * b *
    c

let () =
  a *+ b *+
    c

let () =
  a *. b *.
    c

let () =
  a / b /
    c

let () =
  a /. b /.
    c

let () =
  a % b %
    c

let () =
  a mod b mod
    c

let () =
  a land b land
    c

let () =
  a lor b lor
    c

let () =
  a lxor b lxor
    c

let () =
  a + b +
    c

let () =
  a +. b +.
    c

let () =
  a - b -
    c

let () =
  a -. b -.
    c

let () =
  a = b
  = c

let () =
  a == b
  == c

let () =
  a => b
  => c

let () =
  a < b
  < c

let () =
  a <$> b
  <$> c

let () =
  a > b
  > c

let () =
  a >>= b
  >>= c

let () =
  a |= b
  |= c

let () =
  a &= b
  &= c

let () =
  a $ b
  $ c

let () =
  a != b
  != c

(* Right associative *)

let () =
  a **
    b **
      c

let () =
  a lsl
    b lsl
      c

let () =
  a lsr
    b lsr
      c

let () =
  a asr
    b asr
      c

let () =
  a :: b :: c

let () =
  a @
  b @
  c

let () =
  a @@
  b @@
  c

let () =
  a ^
  b ^
  c

let () =
  a ^=
  b ^=
  c

let () =
  a
  & b
  & c

let () =
  a
  && b
  && c

let () =
  a
  or b
  or c

let () =
  a
  || b
  || c

let () =
  a <-
    b <-
      c

let () =
  a :=
    b :=
      c

let () =
  a;
  b;
  c
@Niols
Copy link
Member Author

Niols commented Dec 1, 2023

Note that we could generate the file without let () = ..., using ;; instead, but Topiary currently handles this separator quite poorly. See #659.

@Niols
Copy link
Member Author

Niols commented Dec 1, 2023

@aspiwack You might have an opinion on this, especially on the interaction of formatting of left-/right-associativity. I remember us discussing this somewhere else as well, with the maintainer of the Tree Sitter grammar in particular.

@aspiwack
Copy link
Member

aspiwack commented Dec 4, 2023

This is the issue you're thinking of, I believe: #541 (comment) .

@Niols
Copy link
Member Author

Niols commented Dec 5, 2023

Exactly, thank you! It also links to #546.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants