{"action":"created","issue":{"url":"https://api.github.com/repos/ocaml/ocaml/issues/715","repository_url":"https://api.github.com/repos/ocaml/ocaml","labels_url":"https://api.github.com/repos/ocaml/ocaml/issues/715/labels{/name}","comments_url":"https://api.github.com/repos/ocaml/ocaml/issues/715/comments","events_url":"https://api.github.com/repos/ocaml/ocaml/issues/715/events","html_url":"https://github.com/ocaml/ocaml/pull/715","id":167474122,"number":715,"title":"-safe-syntax option","user":{"login":"bluddy","id":87875,"avatar_url":"https://avatars.githubusercontent.com/u/87875?v=3","gravatar_id":"","url":"https://api.github.com/users/bluddy","html_url":"https://github.com/bluddy","followers_url":"https://api.github.com/users/bluddy/followers","following_url":"https://api.github.com/users/bluddy/following{/other_user}","gists_url":"https://api.github.com/users/bluddy/gists{/gist_id}","starred_url":"https://api.github.com/users/bluddy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bluddy/subscriptions","organizations_url":"https://api.github.com/users/bluddy/orgs","repos_url":"https://api.github.com/users/bluddy/repos","events_url":"https://api.github.com/users/bluddy/events{/privacy}","received_events_url":"https://api.github.com/users/bluddy/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":50,"created_at":"2016-07-25T21:48:43Z","updated_at":"2016-08-13T12:46:52Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/ocaml/ocaml/pulls/715","html_url":"https://github.com/ocaml/ocaml/pull/715","diff_url":"https://github.com/ocaml/ocaml/pull/715.diff","patch_url":"https://github.com/ocaml/ocaml/pull/715.patch"},"body":"## Proposal\r\nTwo problems presently exist and have been discussed regarding OCaml syntax (see #278 and [this reddit thread](https://www.reddit.com/r/ocaml/comments/4trqbh/more_structured_pattern_matching/)):\r\n\r\n### Problem with If...else\r\nBecause the `if` expression has no terminator, a common issue is that refactoring of code containing an `if` expression can result in incorrect behavior. The scenario generally involves something like\r\n\r\n`if x = 3 then 12`\r\n\r\nLater refactoring may add to the `then` clause:\r\n\r\n`if x = 3 then print_string \"done\"; 12`\r\n\r\nWhich would cause the behavior to be incorrect. Many variations of this problem exist, all of which concern the lack of a terminator. To prevent this issue, many OCaml programmers proactively insert `begin` and `end` into every if expression.\r\n\r\nThis PR changes the `if` syntax to be\r\n\r\n`if x = 3 then 12 end`\r\n\r\nThe small, amortized addition of `end` means that the `if` expression becomes completely safe in this regard, and that there is no need to ever use `begin...end` in this context.\r\n\r\n### Problem with deep matching\r\nDeep matching also suffers from the lack of a terminator. Specifically, for the example\r\n\r\n````\r\n    type t = A | B\r\n    type u = C of t | D\r\n\r\n    let f = function\r\n        | C _ -> 1\r\n        | D -> 2\r\n````\r\n\r\ncan later be augmented with some deep matching, which needs to be protected with parentheses when deep matching:\r\n\r\n````\r\n    let f = function\r\n        | C x -> begin match x with\r\n            | A -> 3\r\n            | B -> 4\r\n            end\r\n        | D -> 2\r\n````\r\n\r\nThis is an annoyance when refactoring, and can sometimes even cause incorrect behavior that isn't picked up by the type checker. As a result, some people proactively couch every `match` in `begin...end`.\r\n\r\nThis PR adds an `end` construct to each one of `match...end`, `function...end`, and `try...with...end`. This change makes it easy to refactor matches and to layer deep matches.\r\n\r\n## Examples\r\n````\r\n    let f l = function\r\n        | C x -> match x with\r\n            | A -> try List.find (fun i -> i = 1) with Not_found -> 3 end \r\n            | B -> 4\r\n            end\r\n        | D -> 2\r\n        end\r\n````\r\n\r\n````\r\n    let g x = if x = 1 then print_string \"yes\"; 1 else print_string \"no\"; 0 end\r\n````\r\n\r\n## Command line argument\r\nThe change to the syntax is only in effect when passing the `-safe-syntax` option. This means that no code is impacted by default except for code which the user specifically chooses to convert to using safe syntax. For example, a company could decide that safe syntax is useful for the sake of reducing potential bugs and simplifying code, but reduce exposure to specific modules.\r\n\r\n## Goals\r\nMy goals in designing the solution presented here were:\r\n\r\n1. Minimize changes to the language. I could have tried to tackle the issue of `function` vs Caml-light's `fun`, or reversing type application. I believe this would have prompted endless bike-shedding, as it should have -- these changes are far more subjective. Instead, I targeted what I consider to be design issues in the syntax that justify breaking backwards-compatibility, and I think these changes should be popular enough that within a few versions, `-safe-syntax` *will be* the default.\r\n\r\n2. Uniformity. `end` is the default terminator for OCaml (see `struct`, `object`, `sig`). While I could see the argument for making `for` and `while` also use `end`, there is also value in differentiation, and doing so would also increase the surface area of the proposed changes, affecting goal 1.\r\n\r\n3. Simplicity. Fixing these 2 problems in the syntax is most easily done at the syntax level, and this change is IMO superior to any option that just makes things more complicated. This patch is inspired by the `-safe-string` option. Within a few versions, I envision all OCaml code being `-safe-syntax` compatible. Additionally, since all forward-looking, refactor-proof OCaml code _already_ wraps `if` and `match` with `begin...end`, the changes to existing codebases are trivial and the changes to the mindset of programmers are trivial as well.\r\n\r\nSome have suggested that backwards-compatibility should trump here, and I disagree. #278 adds to the complexity of the language. Appropriate tools are brittle and hard to design, and not everyone can rely on them.\r\n\r\n## Backwards Compatibility\r\nOf course, changing the syntax in a fundamental way breaks backwards compatibility. I believe this is still the correct approach. For old compilers (due to LTS releases/other reasons), a [conversion tool exists](https://github.com/bluddy/ocaml_syntax_converter), placing the onus on build systems (ocamlbuild/Jenga/omake/ocp-build) to support `-safe-syntax` in old compilers.\r\n\r\nWith this tool, build systems (ocamlbuild, Jenga, ocp-build, omake) could convert every safe-syntax file to unsafe-syntax on the fly and compile it with an older OCaml compiler. In the first stage (when `-safe-syntax` is opt-in in version X), they would do so for specific files that ask to be compiled in `-safe-syntax` mode if the system compiler is earlier than version X. In the future (when `-unsafe-syntax` is opt-in at version Y), they will do so for all files if the system compiler is less than X, or just supply `-safe-syntax` for most files if the version is >= X and < Y.\r\n\r\n## Note\r\nThis PR would benefit from #292, which would allow code sharing between parsers and therefore reduced duplication."},"comment":{"url":"https://api.github.com/repos/ocaml/ocaml/issues/comments/239619178","html_url":"https://github.com/ocaml/ocaml/pull/715#issuecomment-239619178","issue_url":"https://api.github.com/repos/ocaml/ocaml/issues/715","id":239619178,"user":{"login":"paurkedal","id":144070,"avatar_url":"https://avatars.githubusercontent.com/u/144070?v=3","gravatar_id":"","url":"https://api.github.com/users/paurkedal","html_url":"https://github.com/paurkedal","followers_url":"https://api.github.com/users/paurkedal/followers","following_url":"https://api.github.com/users/paurkedal/following{/other_user}","gists_url":"https://api.github.com/users/paurkedal/gists{/gist_id}","starred_url":"https://api.github.com/users/paurkedal/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/paurkedal/subscriptions","organizations_url":"https://api.github.com/users/paurkedal/orgs","repos_url":"https://api.github.com/users/paurkedal/repos","events_url":"https://api.github.com/users/paurkedal/events{/privacy}","received_events_url":"https://api.github.com/users/paurkedal/received_events","type":"User","site_admin":false},"created_at":"2016-08-13T12:46:51Z","updated_at":"2016-08-13T12:46:51Z","body":"I generally like the proposal, except for changing the current if-then which allows structuring the code as\r\n```ocaml\r\nlet rec find x = function\r\n  | Empty -> None\r\n  | Node(l, v, d, r, _) ->\r\n      let c = Ord.compare x v in\r\n      if c < 0 then find x l else\r\n      if c > 0 then find x r else\r\n      d\r\n```\r\nIt should be possible to introduce `if ... then ... end` along with the current `if ... then ... else ...`, though it only solves half the issue with imperative code.\r\n\r\nOn another note, the requirement of `end` can be relaxed for parenthesised expressions, since the compiler can assume it as soon as it hits an end-parenthesis which would otherwise be unbalanced."}}
{"actor":{"display_login":"paurkedal"}}