reasonml / reason

Simple, fast & type safe code that leverages the JavaScript & OCaml ecosystems
http://reasonml.github.io
MIT License
10.12k stars 428 forks source link

Reason V4 [Stack 2/n #2599] [String Template Literals] #2599

Open jordwalke opened 4 years ago

jordwalke commented 4 years ago

Reason Syntax String Template Literals


This is diff 2 in a stack of github PRs: Only examine the most recent commit in this PR to understand what this specific PR accomplishes. The whole stack is:

  1. Reason V4 [Stacked Diff 1/n #2605] [Allow multiple versions of Reason] …
  2. YOU ARE HERE --> Reason V4 [Stacked Diff 2/n #2599] [String Template Literals] …
  3. Reason V4 [Stacked Diff 3/n #2614] [Parse Hashtags for polymorphic va… …
  4. Reason V4 [Stacked Diff 4/n #2619] [Make merlin and rtop respect late… …

This feature implements a planned v4 feature, but in a way that is totally non-breaking and incremental.

String template literals (multiline-form): I was able to make the multiline form of string templates completely non-breaking. It would be a minor version bump, not major. This doesn't implement the final v4 single line version of string templates, which may be a breaking change.

let x = `
  Hello this is a multiline string literal and
  you can inject an expression ${like ++ this()}
`;

This does not conflict with polymorphic variants, because polymorphic variants do not have a newline break after the first backtick.

let poyVariant = `HelloThere;
let multiLineString = `
  hello!
`;

String templates automatically extract the indentation, and normalize it so that you don't have to align text to the zero column (as JS requires you do).

Bonus feature: Non-multiline version of string templates in a non-breaking change.

Okay, this diff actually does implement single line string templates as well. It is not the final v4 form, but it currently works. It turns out that you can use any initial white space after the opening backtick and before the closing backtick - such as a space, and it will be treated the same.

let singleLine = ` Here is a template ${stringToo()}! `;

In the first breaking change (v4), the leading/final space will not be required, and I would not recommend heavily advertising the single-line version in the mean time, but it was easy to support and did not present any syntactic conflict so I included it.

vim-reasonml plugin updated with support:

jordwalke commented 4 years ago

This feature allows directly embedding let expressions inside of interpolations such as:

let commentInLetSequence2 = `
  Okay, the following can accept not just an expression
  but also a "let sequence":
  ${
    let x = 200 + 49;
    string_of_int(x);
  }
  Now wasn't that cool?
`;
jordwalke commented 4 years ago

Force pushed on top of the version angle braces and the feature that records a version inside of the file this PR

jfrolich commented 4 years ago

I did some more thinking. I actually think using """ is probably better in a lot of ways. First, it allows us to seamlessly support markdown in a string (which is actually used quite a bit). Secondly, it's easier when using Reason code snippets in markdown which is also used quite a lot (both within github etc., but also potentially when using the multiline string literal + a decorator to document code -- which might be a nicer way to document than the comment syntax).

I also think we should parse/format it like this:

    let multiLineString = """
      I am a multiline string
      """"
/*    ^ the string separators clearly indicate the beginning of the indentation. */

This is also exactly how multiline strings look like in Python, Swift, and Elixir, so recognizable for a lot of people.

gaku-sei commented 3 years ago

I'm not sure that's the proper place to ask, but is there any plan to have the template strings working with unicode characters? As per the doc file here, it seems the template strings are currently converted to "simple" strings, using the double quotes.

We could have a modifier for this maybe, to make it opt-in, and to use the parsing rules, something like:

let simple = `Created: ${string_of_int(42)}`;
// "Created: " ++ string_of_int(42)}

let unicode = u`Créée: ${string_of_int(42)}`;
// {j|Créée: |j} ++ string_of_int(42)}