fsharp / fslang-suggestions

The place to make suggestions, discuss and vote on F# language and core library features
345 stars 21 forks source link

Longer quoted strings #1301

Open Happypig375 opened 1 year ago

Happypig375 commented 1 year ago

I propose we add longer quoted strings (7+ quotes since 6 quotes specify an empty triple-quoted string) for when we need to embed source code that contains triple quoted strings. It is in line with C#'s design of raw string literals with variable length of starting quotes. Here, we can follow C#'s design of the multiline literal with the end delimiter specifying the indentation size (must be whitespaces, not any other character):

let fsharpCode = """""""
let string = $"""{String.replicate 3 "Hello World!\n"}"""
"""""""
let fsharpCode' = """""""
     let string = $"""{String.replicate 3 "Hello World!\n"}"""
     """""""
fsharpCode = fsharpCode' // true

The newlines directly next to the delimiters will not be included in the string. Therefore, the string has the following content without preceding or succeeding newlines:

let string = $"""{String.replicate 3 "Hello World!\n"}"""

Since 7 quotes are already quite long, we won't allow single-line versions of it. So these are illegal:

let fsharpCode = """""""let string = $"""{String.replicate 3 "Hello World!\n"}""" """"""" // error - no single line version
let fsharpCode' = """""""
     let string = $"""{String.replicate 3 "Hello World!\n"}""" """"""" // error - ending delimiter must be on its own line
let fsharpCode'' = """""""let
    string = $"""{String.replicate 3 "Hello World!\n"}"""
    """"""" // error - starting delimiter must be followed by a newline

The minimum lines that a longer quoted string must occupy is 3:

let empty = """""""
""""""" // error - too short - leave some space for adding content!
let empty' = """""""

""""""" // ok

This is because the starting delimiter requires a newline after, and the ending delimiter requires a newline before.

Interpolation will work:

let fsharpCode = $$"""""""
let string = $"""{String.replicate %%d{{3}} "Hello World!\n"}"""
"""""""

and produces the same string as before. The existing way of approaching this problem in F# is to use runtime string manipulation which is less visually appealing and costs performance.

Pros and Cons

The advantages of making this adjustment to F# are

  1. Alignment with C#
  2. Convenience
  3. Visual appeal compared to triple quoted strings

The disadvantage of making this adjustment to F# is the learning complexity and implementation and maintenance costs.

Extra information

Estimated cost (XS, S, M, L, XL, XXL): S to M

Related suggestions:

Affidavit (please submit!)

Please tick these items by placing a cross in the box:

Please tick all that apply:

For Readers

If you would like to see this issue implemented, please click the :+1: emoji on this issue. These counts are used to generally order the suggestions by engagement.

Happypig375 commented 1 year ago

We may also add language markers as specified in this C# proposal.

let mystr = """""""html
    <html>
        <body>
            <h1>Heading</h1>
            <p>Paragraph</p>
        </body>
    </html>
    """""""
cr3wdayt5p commented 7 months ago

This is a nice suggestion. We get the desirable feature of correct indentation for long strings while keeping the language backward compatible – and at 7 (or more) quotes the behavior is identical to C#.

F#:

let myStr =
    """""""
    <html>
        <body>
            <h1>Heading</h1>
            <p>Paragraph</p>
        </body>
    </html>
    """""""

C#:

var myStr =
    """""""
    <html>
        <body>
            <h1>Heading</h1>
            <p>Paragraph</p>
        </body>
    </html>
    """"""";

However I would suggest that this proposal is focused on nicely quoted strings, and that syntax highlighting is kept to a separate proposal that would work on all types of strings, e.g. standardize Rider's "language injection" feature:

let myStr1 =
    // lang=html
    "<b>hello</b>"

let myStr2 =
    // lang=html
    """<b>hello</b>"""

let myStr3 =
    // lang=html
    """""""
    <b>hello</b>
    """""""
cr3wdayt5p commented 7 months ago

I actually think this suggestion should be implemented solely due the readability benefit this would bring to the Fantomas test cases. Imagine how much nicer a file like this would read with correctly indented multiline strings.