fslang-suggestions icon indicating copy to clipboard operation
fslang-suggestions copied to clipboard

Longer quoted strings

Open Happypig375 opened this issue 2 years ago • 7 comments

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:

  • https://github.com/dotnet/csharplang/issues/4304

Affidavit (please submit!)

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

  • [x] This is not a question (e.g. like one you might ask on StackOverflow) and I have searched StackOverflow for discussions of this issue
  • [x] This is a language change and not purely a tooling change (e.g. compiler bug, editor support, warning/error messages, new warning, non-breaking optimisation) belonging to the compiler and tooling repository
  • [x] This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it
  • [x] I have searched both open and closed suggestions on this site and believe this is not a duplicate

Please tick all that apply:

  • [x] This is not a breaking change to the F# language design
  • [x] I or my company would be willing to help implement and/or test this

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 avatar Aug 05 '23 16:08 Happypig375

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>
    """""""

Happypig375 avatar Aug 06 '23 06:08 Happypig375

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 avatar Mar 04 '24 22:03 cr3wdayt5p

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.

cr3wdayt5p avatar Mar 04 '24 23:03 cr3wdayt5p

It's been a pain point for FSAC test cases as well. We've made a whole helper library for dealing with the current indentation-appended strings.

baronfel avatar Sep 04 '25 15:09 baronfel

If approved I might be able to implemented as some point.

edgarfgp avatar Sep 04 '25 19:09 edgarfgp

I understand the need, and I assume the test cases for source code related tools are the biggest motivator here. The workaround is either runtime helpers for trimming left-side whitespace, or storing the source code in a separate text file (which has also the added advantage of not causing a recompilation if the code is changed, btw.).

I do not want to immediately approve this, even though this is perfectly doable and if starting at 7 " symbols, does not risk backwards compat issues. Reason is, there are already too many forms of strings and I do not like the idea of having YET another one. Especially of this yet another one would not only allow more quotes inside (this feels OK), but would also have a different treatment to left-side whitespace compared to all other strings.

Also, isn't the source-code as string motivation a never ending one? Triple quote strings were added to easily create strings that have a quote in them. If 7-quote strings are added to the language, how would tests for code with them be created? ( I realize this asks for a general feature with 7+ quotes going up to many. But maybe it is a signal to store the content separately?)

T-Gro avatar Sep 05 '25 09:09 T-Gro

Also, isn't the source-code as string motivation a never ending one? Triple quote strings were added to easily create strings that have a quote in them. If 7-quote strings are added to the language, how would tests for code with them be created? ( I realize this asks for a general feature with 7+ quotes going up to many. But maybe it is a signal to store the content separately?)

I think the variable length approach solves this in an elegant way (https://github.com/dotnet/csharplang/blob/345fa10e4f19b5510b60cbc4822ad953213a906f/proposals/csharp-11.0/raw-string-literal.md).

E.g. if you have a source code snippet with 8 quotes in it then you wrap it in 9 (or more) quotes.

The source code use case if often lots of small snippets (few lines). So having them directly in the test code is highly preferable compared to maintaining many separate tiny files and linking them correctly (it would also complicate PR reviews greatly – including AI assisted ones).

cr3wdayt5p avatar Sep 05 '25 09:09 cr3wdayt5p