[Review] SE-0168: Multi-Line String Literals

On a separate note, I'd like to bring up the de-indentation behavior I described earlier again. I still feel that having the position of the closing delimiter determine how much whitespace is de-indented is not very natural or intuitive, since I don't think there is any precedent in standard Swift styling to indent a closing delimiter to the same level as its content.

String literal delimiters are very different from other delimiters because they switch the parser into a different mode where characters are interpreted in vastly different ways, and every character has a significant meaning. For instance, it's good practice to put either a space, or a newline and indentation, between array and dictionary literal delimiters or curly brackets and their content, but this is not possible with a string literal because the space would count as part of the content. This is the same way: you can't outdent because whitespace is significant inside a string literal, so it would change the meaning.

I think that this probably seems way weirder on paper than it really is in practice. I recommend that you try it and see how it feels.

Stripping the most common whitespace possible from each line seems to be a much more intuitive and flexible solution in terms of formatting, and it's still compatible with the proposed formatting if that's anyone's preference.

I discuss this at length in the Rationale section for indentation stripping. If you'll forgive me for quoting myself:

We could instead use an algorithm where the longest common whitespace prefix is removed from all lines; in well-formed code, that would produce the same behavior as this algorithm. But when not well-formed—when one line was accidentally indented less than the delimiter, or when a user mixed tabs and spaces accidentally—it would lead to valid, but incorrect and undiagnosable, behavior. For instance, if one line used a tab and other lines used spaces, Swift would not strip indentation from any of the lines; if most lines were indented four spaces, but one line was indented three, Swift would strip three spaces of indentation from all lines. And while you would still be able to create a string with all lines indented by indenting the closing delimiter less than the others, many users would never discover this trick.

Let me provide an example to illustrate what I'm talking about. Suppose you want to say this:

  ····xml += """\↵
  ············<book id="bk\(id)">↵
  ················<author>\(author)</author>↵
  ················<title>\(title)</title>↵
  ················<genre>\(genre)</genre>↵
  ················<price>\(price)</price>↵
  ············</book>↵
  ············"""↵

But instead, you miss just one little insignificant character:

  ····xml += """\↵
  ···········<book id="bk\(id)">↵
  ················<author>\(author)</author>↵
  ················<title>\(title)</title>↵
  ················<genre>\(genre)</genre>↵
  ················<price>\(price)</price>↵
  ············</book>↵
  ············"""↵

This is the kind of mistake you will almost certainly never notice by hand inspection. You probably can't see the mistake without looking very carefully—and this is with invisible whitespace replaced with visible dots! But in the least-common-whitespace design, it's perfectly valid, and generates this:

  <book id="bk\(id)">↵
  ·····<author>\(author)</author>↵
  ·····<title>\(title)</title>↵
  ·····<genre>\(genre)</genre>↵
  ·····<price>\(price)</price>↵
  ·</book>↵
  ·

That is not what you wanted. I'm pretty sure it's almost *never* what you want. But it's valid, it's going to be accepted, and it's going to affect every single line of the literal in a subtle way. (Plus the next line, thanks to that trailing space!) It's not something we can warn about, either, because it's perfectly valid. To fix it, you'll have to notice it's wrong and then work out why that happened.

In the proposed design, on the other hand, we have a single source of truth for indentation: the last line tells us how much we should remove. That means we can actually call a mistake a mistake. The very same example, run through the proposed algorithm, produces this, plus a warning on the first line:

  ···········<book id="bk\(id)">↵
  ····<author>\(author)</author>↵
  ····<title>\(title)</title>↵
  ····<genre>\(genre)</genre>↵
  ····<price>\(price)</price>↵
  </book>↵

Notice that there is only one line that comes out incorrectly, that it's the line which has the mistake, that the mistake is large and noticeable in the output, *and* that we were also able to emit a compile-time warning pointing to the exact line of code that was mistaken. That outcome is night-and-day better.

Now consider mixed tabs and spaces:

  ····xml += """\↵
  ············<book id="bk\(id)">↵
  ················<author>\(author)</author>↵
  ········⇥ ····<title>\(title)</title>↵
  ················<genre>\(genre)</genre>↵
  ················<price>\(price)</price>↵
  ············</book>↵
  ············"""↵

(I'm assuming a tab stop of 4, so mentally adjust that example if you need to.)

With your design, the compiler happily removes the common whitespace and writes code which does this:

  ····<book id="bk\(id)">↵
  ········<author>\(author)</author>↵
  ⇥ ····<title>\(title)</title>↵
  ········<genre>\(genre)</genre>↵
  ········<price>\(price)</price>↵
  ····</book>↵
  ····

Once again, every line is affected—including lines after this snippet, since there are spaces after the last newline. Once again, there can be no warning. You'll need to notice the problem and then figure out what happened.

By contrast, with the proposed design, you get this, plus a warning:

  <book id="bk\(id)">↵
  ····<author>\(author)</author>↵
  ········⇥ ····<title>\(title)</title>↵
  ····<genre>\(genre)</genre>↵
  ····<price>\(price)</price>↵
  </book>↵

Once again, the only line that's affected is the bad line, *and* you get a warning. In this case, I think the warning could probably point you to the exact *character* that causes the problem.

Basically, common-whitespace-prefix makes the compiler act like a dumb computer that does what you say, not what you want. The proposed algorithm makes the compiler act like a smart human that notices when you ask for something that doesn't make sense and tells you about the problem.

(Also note how, if you want a trailing newline, you still end up having the delimiter on a separate line aligned with the other text anyway! Stripping the common whitespace prefix in practice still ends up looking exactly the same as what you object to.)

The only functional limitation that I see is that if you can't have leading whitespace in the interpreted string if you actually want that. That doesn't seem like a very important use case to me,

We showed an example of this being done in the Rationale section, and it was a *very* plausible example. I don't think it's rare or unnecessary at all; I think it's a really important use case, particularly for generating pretty-printed code or markup.

but if we think it is important, it could be supported by something like having a backslash in the leading whitespace at the location where it should be preserved from.

There are good reasons not to allow backslashing of several different varieties of whitespace, and people were really unhappy with designs that required them to modify every line of text. I think this is a non-starter.

If we're set on the proposed behavior, have we considered what happens if the closing delimiter goes beyond the non-whitespace content of the string?

let string = """
    aa
    bb
    cc
     """

Does it strip the non-whitespace characters? Does it strip up to the non-whitespace characters? Does it generate an error?

It strips nothing and generates a warning on each offending line (but not an error, because whitespace problems are usually minor enough that there's no need to interrupt your debugging to fix some indentation). This was covered in the proposal.

(In an example like this, where every line is less indented than the delimiter, we might emit a different warning suggesting that the delimiter's indentation is wrong. That's a QoI issue, though, not the kind of thing we need to cover in a proposal.)

···

On Apr 12, 2017, at 11:58 AM, Jarod Long via swift-evolution <swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies

Agree. I prefer the new rules over the old, but considering common use
cases, stripping the leading and trailing newline makes for a more pleasant
experience than not stripping either of them.

I think that is generally worth prioritizing over a simpler algorithm or
even accommodating more styles. Moreover, a user who wants a trailing or
leading newline merely types an extra one if there is newline stripping, so
no use cases are made difficult, only a very common one is made more
ergonomic.

···

On Wed, Apr 12, 2017 at 09:52 Thorsten Seitz via swift-evolution < swift-evolution@swift.org> wrote:

> Am 12.04.2017 um 15:40 schrieb Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org>:
>
> Hey folks,
>
>
> We've revised the proposal again. The main difference: You no longer
need an initial newline to enable indentation stripping, and stripping no
longer removes that newline even if it is present. (Adrian Zubarev and I
believe some others argued for this.) We

Hmm, not sure if I like these changes. I expect that almost all strings
won't begin with a newline and a majority won’t end with a newline. The new
design would require a leading backslash almost all the time and a trailing
backslash often, which is ugly:

let mystring = "““\
    text text
    text text\
    "““

-Thorsten

> disagreed with this at first, but it made more sense as we thought about
it more. There are a few things we like about it:
>
> 1. The rules and algorithm are simpler.
> 2. It accommodates more coding styles.
> 3. Every non-escaped newline in the literal now creates a
corresponding newline in the resulting string.
> 4. it's easy to get the old behavior back by backslashing the
leading newline.
>
> Unfortunately, I think this precludes stripping the trailing newline by
default, but I think this is ultimately a simpler and better approach than
the previous draft.
>
> Other changes:
>
> * We realized we needed to make closing delimiter matching a
little more complicated if we wanted to allow one or two adjacent
double-quote characters that were part of the literal's contents. Oops.
> * Tabs aren't actually allowed in ordinary string literals, so we
now explicitly mention that as a difference between the two types.
> * We wrote some tests for the prototype (though they haven't been
updated for this new version yet).
> * There were some other wording changes, particularly in the
indentation stripping rationale, but nothing that affects the actual design.
>
> I understand John is working on a new version of his toolchain so people
can play with the prototype. We hope to have that ready for you all soon.
>
> Let us know what you think of the revisions!
>
> --
> Brent Royal-Gordon
> Architechies
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Thanks Brent, I really appreciate the thoughtful response. Apologies for anything I overlooked previously.

I agree with most of your points, although I still find myself preferring the common-whitespace logic and leading/trailing newline stripping when considering the pros and cons. It doesn't seem likely to gain traction though, so I won't spend more time on it.

Thanks again!

Jarod

···

On Apr 12, 2017, 16:35 -0700, Brent Royal-Gordon <brent@architechies.com>, wrote:

> On Apr 12, 2017, at 11:58 AM, Jarod Long via swift-evolution <swift-evolution@swift.org> wrote:
>
> On a separate note, I'd like to bring up the de-indentation behavior I described earlier again. I still feel that having the position of the closing delimiter determine how much whitespace is de-indented is not very natural or intuitive, since I don't think there is any precedent in standard Swift styling to indent a closing delimiter to the same level as its content.

String literal delimiters are very different from other delimiters because they switch the parser into a different mode where characters are interpreted in vastly different ways, and every character has a significant meaning. For instance, it's good practice to put either a space, or a newline and indentation, between array and dictionary literal delimiters or curly brackets and their content, but this is not possible with a string literal because the space would count as part of the content. This is the same way: you can't outdent because whitespace is significant inside a string literal, so it would change the meaning.

I think that this probably seems way weirder on paper than it really is in practice. I recommend that you try it and see how it feels.

> Stripping the most common whitespace possible from each line seems to be a much more intuitive and flexible solution in terms of formatting, and it's still compatible with the proposed formatting if that's anyone's preference.

I discuss this at length in the Rationale section for indentation stripping. If you'll forgive me for quoting myself:

> We could instead use an algorithm where the longest common whitespace prefix is removed from all lines; in well-formed code, that would produce the same behavior as this algorithm. But when not well-formed—when one line was accidentally indented less than the delimiter, or when a user mixed tabs and spaces accidentally—it would lead to valid, but incorrect and undiagnosable, behavior. For instance, if one line used a tab and other lines used spaces, Swift would not strip indentation from any of the lines; if most lines were indented four spaces, but one line was indented three, Swift would strip three spaces of indentation from all lines. And while you would still be able to create a string with all lines indented by indenting the closing delimiter less than the others, many users would never discover this trick.
Let me provide an example to illustrate what I'm talking about. Suppose you want to say this:

····xml += """\↵
············<book id="bk\(id)">↵
················<author>\(author)</author>↵
················<title>\(title)</title>↵
················<genre>\(genre)</genre>↵
················<price>\(price)</price>↵
············</book>↵
············"""↵

But instead, you miss just one little insignificant character:

····xml += """\↵
···········<book id="bk\(id)">↵
················<author>\(author)</author>↵
················<title>\(title)</title>↵
················<genre>\(genre)</genre>↵
················<price>\(price)</price>↵
············</book>↵
············"""↵

This is the kind of mistake you will almost certainly never notice by hand inspection. You probably can't see the mistake without looking very carefully—and this is with invisible whitespace replaced with visible dots! But in the least-common-whitespace design, it's perfectly valid, and generates this:

<book id="bk\(id)">↵
·····<author>\(author)</author>↵
·····<title>\(title)</title>↵
·····<genre>\(genre)</genre>↵
·····<price>\(price)</price>↵
·</book>↵
·

That is not what you wanted. I'm pretty sure it's almost *never* what you want. But it's valid, it's going to be accepted, and it's going to affect every single line of the literal in a subtle way. (Plus the next line, thanks to that trailing space!) It's not something we can warn about, either, because it's perfectly valid. To fix it, you'll have to notice it's wrong and then work out why that happened.

In the proposed design, on the other hand, we have a single source of truth for indentation: the last line tells us how much we should remove. That means we can actually call a mistake a mistake. The very same example, run through the proposed algorithm, produces this, plus a warning on the first line:

···········<book id="bk\(id)">↵
····<author>\(author)</author>↵
····<title>\(title)</title>↵
····<genre>\(genre)</genre>↵
····<price>\(price)</price>↵
</book>↵

Notice that there is only one line that comes out incorrectly, that it's the line which has the mistake, that the mistake is large and noticeable in the output, *and* that we were also able to emit a compile-time warning pointing to the exact line of code that was mistaken. That outcome is night-and-day better.

Now consider mixed tabs and spaces:

····xml += """\↵
············<book id="bk\(id)">↵
················<author>\(author)</author>↵
········⇥ ····<title>\(title)</title>↵
················<genre>\(genre)</genre>↵
················<price>\(price)</price>↵
············</book>↵
············"""↵

(I'm assuming a tab stop of 4, so mentally adjust that example if you need to.)

With your design, the compiler happily removes the common whitespace and writes code which does this:

····<book id="bk\(id)">↵
········<author>\(author)</author>↵
⇥ ····<title>\(title)</title>↵
········<genre>\(genre)</genre>↵
········<price>\(price)</price>↵
····</book>↵
····

Once again, every line is affected—including lines after this snippet, since there are spaces after the last newline. Once again, there can be no warning. You'll need to notice the problem and then figure out what happened.

By contrast, with the proposed design, you get this, plus a warning:

<book id="bk\(id)">↵
····<author>\(author)</author>↵
········⇥ ····<title>\(title)</title>↵
····<genre>\(genre)</genre>↵
····<price>\(price)</price>↵
</book>↵

Once again, the only line that's affected is the bad line, *and* you get a warning. In this case, I think the warning could probably point you to the exact *character* that causes the problem.

Basically, common-whitespace-prefix makes the compiler act like a dumb computer that does what you say, not what you want. The proposed algorithm makes the compiler act like a smart human that notices when you ask for something that doesn't make sense and tells you about the problem.

(Also note how, if you want a trailing newline, you still end up having the delimiter on a separate line aligned with the other text anyway! Stripping the common whitespace prefix in practice still ends up looking exactly the same as what you object to.)

> The only functional limitation that I see is that if you can't have leading whitespace in the interpreted string if you actually want that. That doesn't seem like a very important use case to me,

We showed an example of this being done in the Rationale section, and it was a *very* plausible example. I don't think it's rare or unnecessary at all; I think it's a really important use case, particularly for generating pretty-printed code or markup.

> but if we think it is important, it could be supported by something like having a backslash in the leading whitespace at the location where it should be preserved from.

There are good reasons not to allow backslashing of several different varieties of whitespace, and people were really unhappy with designs that required them to modify every line of text. I think this is a non-starter.

> If we're set on the proposed behavior, have we considered what happens if the closing delimiter goes beyond the non-whitespace content of the string?
>
> let string = """
> aa
> bb
> cc
> """
>
> Does it strip the non-whitespace characters? Does it strip up to the non-whitespace characters? Does it generate an error?

It strips nothing and generates a warning on each offending line (but not an error, because whitespace problems are usually minor enough that there's no need to interrupt your debugging to fix some indentation). This was covered in the proposal.

(In an example like this, where every line is less indented than the delimiter, we might emit a different warning suggesting that the delimiter's indentation is wrong. That's a QoI issue, though, not the kind of thing we need to cover in a proposal.)

--
Brent Royal-Gordon
Architechies

Hey folks,

We've revised the proposal again. The main difference: You no longer need an initial newline to enable indentation stripping, and stripping no longer removes that newline even if it is present. (Adrian Zubarev and I believe some others argued for this.) We

Hmm, not sure if I like these changes. I expect that almost all strings won't begin with a newline and a majority won’t end with a newline. The new design would require a leading backslash almost all the time and a trailing backslash often, which is ugly:

let mystring = "““\
     text text
     text text\
     "““

Agree with Thorsten. This is just ugly syntax for commonly used case.
Why not simple rule that content of multi-line string is all lines *between* leading and trailing """ ? So, as was discussed, or you have single line """text""" or multiline, where opening and closing """ should be on separate lines with text, and text is lines strictly between.
Even more, I feel such solution will be more consistent, as trailing triple quotes does not inject \n symbol -> so leading """ also should not inject it. In this case """ is just a marker of start/end of multi-line string. Simple model, nice-looking code for common cases, can tune emitting of \n symbol as you wish. No?

···

On 12.04.2017 17:52, Thorsten Seitz via swift-evolution wrote:

Am 12.04.2017 um 15:40 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org>:

-Thorsten

disagreed with this at first, but it made more sense as we thought about it more. There are a few things we like about it:

  1. The rules and algorithm are simpler.
  2. It accommodates more coding styles.
  3. Every non-escaped newline in the literal now creates a corresponding newline in the resulting string.
  4. it's easy to get the old behavior back by backslashing the leading newline.

Unfortunately, I think this precludes stripping the trailing newline by default, but I think this is ultimately a simpler and better approach than the previous draft.

Other changes:

  * We realized we needed to make closing delimiter matching a little more complicated if we wanted to allow one or two adjacent double-quote characters that were part of the literal's contents. Oops.
  * Tabs aren't actually allowed in ordinary string literals, so we now explicitly mention that as a difference between the two types.
  * We wrote some tests for the prototype (though they haven't been updated for this new version yet).
  * There were some other wording changes, particularly in the indentation stripping rationale, but nothing that affects the actual design.

I understand John is working on a new version of his toolchain so people can play with the prototype. We hope to have that ready for you all soon.

Let us know what you think of the revisions!

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Actually this would be inconsistent. The lines in between the delimiters should always add an implicit new line if not told otherwise with a backslash. If that wouldn’t be the case than you won’t have any precision in the last line you have there.

Assume you want to concatenate this string:

foo
bar baz
To do so you’d need extra spaces in your " baz" string, because of the inconsistency. However the constant version I’m suggesting is more precise.

let myString = """
    foo
    bar \
    """
     
print(myString + "baz")
The last implicit new line which one would need to escape is a model artifact, but it’s consistent that way.

The extra new line one would ever need to add manually would be only at the top of a multi-line string.

// v1:

"""

foo
bar\
"""

// v2:
"""
\n\
foo
bar\
"""

// v3:
"""
\nfoo
bar\
"""

···

--
Adrian Zubarev
Sent with Airmail

Am 12. April 2017 um 19:41:14, Ricardo Parada via swift-evolution (swift-evolution@swift.org) schrieb:

Hi all,

I agree as well, I think we should make optimize for the most common case of multi-line strings. A rule that says strip the first leading newline as well as the trailing newline. So it's almost back to where Brent started with the addition of removing the trailing newline.

Borrowing Adrian's example, I could just have this:

let myReallyLongXMLConstantName = """
    <?xml version="1.0"?>
    <catalog>
        <book id="bk101" empty="">
            <author>John Doe</author>
            <title>XML Developer's Guide</title>
            <genre>Computer</genre>
            <price>44.95</price>
        </book>
    </catalog>
    """
If somebody wants the last line to include a newline at the end, they can just add a \n at the end or an empty line.

So if I do this:
print(myReallyLongXMLConstantName)
print("Text right below")
It would output this:

<?xml version="1.0"?>
<catalog>
<book id="bk101" empty="">
<author>John Doe</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
</book>
</catalog>
Test right below

Without removing the trailing newline then it would print like this:

<?xml version="1.0"?>
<catalog>
<book id="bk101" empty="">
<author>John Doe</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
</book>
</catalog>

Test right below

On Apr 12, 2017, at 12:48 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

Agree. I prefer the new rules over the old, but considering common use cases, stripping the leading and trailing newline makes for a more pleasant experience than not stripping either of them.

I think that is generally worth prioritizing over a simpler algorithm or even accommodating more styles. Moreover, a user who wants a trailing or leading newline merely types an extra one if there is newline stripping, so no use cases are made difficult, only a very common one is made more ergonomic.
On Wed, Apr 12, 2017 at 09:52 Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

Am 12.04.2017 um 15:40 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org>:

Hey folks,

We've revised the proposal again. The main difference: You no longer need an initial newline to enable indentation stripping, and stripping no longer removes that newline even if it is present. (Adrian Zubarev and I believe some others argued for this.) We

Hmm, not sure if I like these changes. I expect that almost all strings won't begin with a newline and a majority won’t end with a newline. The new design would require a leading backslash almost all the time and a trailing backslash often, which is ugly:

let mystring = "““\
text text
text text\
"““

-Thorsten

disagreed with this at first, but it made more sense as we thought about it more. There are a few things we like about it:

   1\. The rules and algorithm are simpler\.
   2\. It accommodates more coding styles\.
   3\. Every non\-escaped newline in the literal now creates a corresponding newline in the resulting string\.
   4\. it&#39;s easy to get the old behavior back by backslashing the leading newline\.

Unfortunately, I think this precludes stripping the trailing newline by default, but I think this is ultimately a simpler and better approach than the previous draft.

Other changes:

   \* We realized we needed to make closing delimiter matching a little more complicated if we wanted to allow one or two adjacent double\-quote characters that were part of the literal&#39;s contents\. Oops\.
   \* Tabs aren&#39;t actually allowed in ordinary string literals, so we now explicitly mention that as a difference between the two types\.
   \* We wrote some tests for the prototype \(though they haven&#39;t been updated for this new version yet\)\.
   \* There were some other wording changes, particularly in the indentation stripping rationale, but nothing that affects the actual design\.

I understand John is working on a new version of his toolchain so people can play with the prototype. We hope to have that ready for you all soon.

Let us know what you think of the revisions!

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Finally.. a new Xcode toolchain <http://johnholdsworth.com/swift-LOCAL-2017-04-12-a-osx.tar.gz&gt; is available largely in sync with the proposal as is.
(You need to restart Xcode after selecting the toolchain to restart SourceKit)

I personally am undecided whether to remove the first line if it is empty. The new
rules are more consistent but somehow less practical. A blank initial line is almost
never what a user would want and I would tend towards removing it automatically.
This is almost what a user would it expect it to do.

I’m less sure the same applies to the trailing newline. If this is a syntax for
multi-line strings, I'd argue that they should normally be complete lines -
particularly since the final newline can so easily be escaped.

        let longstring = """\
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod \
            tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, \
            quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\
            """

        print( """\
            Usage: myapp <options>
            
            Run myapp to do mything
            
            Options:
            -myoption - an option
            """ )

(An explicit “\n" in the string should never be stripped btw)

Can we have a straw poll for the three alternatives:

1) Proposal as it stands - no magic removal of leading/training blank lines.
2) Removal of a leading blank line when indent stripping is being applied.
3) Removal of leading blank line and trailing newline when indent stripping is being applied.

My vote is for the pragmatic path: 2)

(The main intent of this revision was actually removing the link between how the
string started and whether indent stripping was applied which was unnecessary.)

···

On 12 Apr 2017, at 17:48, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

Agree. I prefer the new rules over the old, but considering common use cases, stripping the leading and trailing newline makes for a more pleasant experience than not stripping either of them.

I think that is generally worth prioritizing over a simpler algorithm or even accommodating more styles. Moreover, a user who wants a trailing or leading newline merely types an extra one if there is newline stripping, so no use cases are made difficult, only a very common one is made more ergonomic.

That’s already a huge part of the proposal. If I understood the indent algorithm correctly than it should already support everyones preferences for their indent style. The indent is determined by the prefix indent before the closing delimiter.

The example you proved should, for my understanding emit 3 warnings that each line in between the delimiter has wrong ident. It also could help you with fix-its to fix that issue and add another whitespace of tab, depending on which indent prefix the closing delimiter has.

The string itself should be equivalent to "aa\nbb\ncc\n" even if you have warnings to fix.

···

--
Adrian Zubarev
Sent with Airmail

Am 12. April 2017 um 21:00:08, Jarod Long via swift-evolution (swift-evolution@swift.org) schrieb:

The only functional limitation that I see is that if you can't have leading whitespace in the interpreted string if you actually want that. That doesn't seem like a very important use case to me, but if we think it is important, it could be supported by something like having a backslash in the leading whitespace at the location where it should be preserved from.

If we're set on the proposed behavior, have we considered what happens if the closing delimiter goes beyond the non-whitespace content of the string?

let string = """
aa
bb
cc
"""

Does it strip the non-whitespace characters? Does it strip up to the non-whitespace characters? Does it generate an error?

Hey folks,

We've revised the proposal again. The main difference: You no longer need an initial newline to enable indentation stripping, and stripping no longer removes that newline even if it is present. (Adrian Zubarev and I believe some others argued for this.) We

Hmm, not sure if I like these changes. I expect that almost all strings won't begin with a newline and a majority won’t end with a newline. The new design would require a leading backslash almost all the time and a trailing backslash often, which is ugly:

let mystring = "““\
   text text
   text text\
   "““

(I think I've read the whole thread, but it can be easy to miss parts on the phone. I think this is different than what's already been discussed... sorry if this has already been suggested and I just missed it.)

What having this input:

let mystring = "““
   text text
   text text
   "““

yielding this output:

text text
text text

And this input:

let mystring = "““/
   text text
   text text
   /"““

yielding this output:

text text
text text

(Sorry about not putting in all the symbols... I don't know a way to type them on my phone)

If you think of the "/" as more of a "toggle whether the next character is escaped" instead of specifically "escape the next character", it makes sense (kinda)... or the outputs could be reversed. Either way, it allows easy access to both options and preserves the "paste-in block of text as-is" goal.

- Dave Sweeris

···

On Apr 12, 2017, at 07:52, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

Am 12.04.2017 um 15:40 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org>:

  • What is your evaluation of the proposal?

I'm a -1 for several reasons, mostly subjective but still. First thing is that I'm generally not comfortable with encouraging the use of multi-line strings at all. These are things that usually should be templated or localised, and personally I don't see what's inconvenient about concatenating on those occasions where you can't (or just don't want to); I'm actually of the opinion that this is the kind of thing that should be awkward and annoying, to encourage developers to think about what they're doing.

IMHO, there are plenty of uses for multi-line strings that are entire valid and acceptable. SQL queries is the example I encounter the most in my day-to-day work. And concatenating makes working with them very cumbersome.

Maybe, but with SQL I long moved on to using placeholders for variables, which makes most statements fairly compact. And in fact these days I almost entirely use stored procedures, so most SQL statements I work with are just enough to call a procedure.

I use placeholders, but there's no way a 4-5 join query fits on a line even then. And those can become fairly common quickly.

Even so, while that may be an argument for the need for multi-line strings it's a use case that would still be better handled by continuation quotes IMO, but again, they don't offer so much added convenience over concatenation to really be worth it.

Why would it be a use case for continuation quotes instead of multi-line strings? I don't follow.

Also, just wanted to add, but those arguing against continuation quotes on the basis of pasting into Swift; you already can paste into Swift using regular double quotes

I don't understand. If I paste a multi line SQL query between double quotes in Swift, the resulting codes does not compile.

···

Sent from my iPhone

On 7 Apr 2017, at 12:17, Haravikk <swift-evolution@haravikk.me> wrote:

On 6 Apr 2017, at 21:47, David Hart <david@hartbit.com> wrote:

On 6 Apr 2017, at 22:34, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:
On 6 Apr 2017, at 20:35, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

, you just don't get to indent text and have the indentation magically handled for you. It's the magic handling of indentation that makes me most uncomfortable about the main proposal. This is why I mentioned the idea of using a compiler directive which would be more explicit in what's going on, as well as being more discoverable as it would provide something easy to search for. Like so:

let foo = #trimleft("
  foo
    bar
  baz
")

Becoming (behind the scenes):

let foo = "foo
  bar
baz"

It has the same benefits as heredocs but without any new syntax, and someone new to the language can just search for "Swift #trimleft" (or whatever it'd be called) to find out what it does exactly.

To be clear, I don't think that it's a particularly powerful footgun. It's just perpetuating the unfortunate status quo on injection vulnerabilities. C# was able to eliminate the whole class of SQL injections by introducing a convenient and powerful syntax for queries that is not string-based. I think that Swift should be moving in that direction, especially since no one has made any other case for long strings so far.

For JSON, it's easy to imagine something like `let json: JsonObject = ["a": "b", "c": ["d": 3]]` (to take Kevin's example) being a safer alternative than long strings, with or without interpolation. Reflection could also be used to serialize objects.

For XML, I know that you have this XMLString idea, but I think that it would be very complex to implement in practice. XML has several different contexts in which escaping has to be different. For instance, you shouldn't escape the same things in an attribute value as in a comment, or in an XML text node, or in a CDATA node, and that means that you have to be aware of what you're looking for at the point where interpolation happens. It's also possible to come up with uncheckable/incorrect cases (like `<foo \(bar)>`), meaning that it either has to accept anything in some cases or be failable (and besides, "just remove :XMLString and it works!"). Until Swift gets compile-time evaluation of expression, this cannot be a compile-time error.

···

Le 9 avr. 2017 à 19:44, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> a écrit :

On Apr 9, 2017, at 9:29 AM, John Holdsworth via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Perhaps we can find common ground on 1) and 2) and even 3) with a view to resubmitting if there is time. Seems mostly like we just need to discuss the delimiter further and decide whether the indent trimming is a bug or a feature to keep moving and not let another year slip by.

Honestly, I think this is a little premature. If I had to summarize this thread, I think what I'm seeing is:

  1. We wish the proposal were more specific on a few points, like the de-indenting algorithm.

  2. We have lots of different pet syntaxes and preferences.

  3. But most of us are still in favor of accepting the proposal.

To back up that last point, I ran through the thread and tried to quickly figure out what everyone was thinking. These people seem to be opposed to the proposal:

  1. Haravikk doesn't like the de-indenting and seems iffy on multiline strings in general.
  2. David Waite wants a suite of different, orthogonal string literal features to get enough flexibility.
  3. Félix Cloutier is worried that supporting interpolation makes this feature a powerful footgun.
  4. Adrian Zubarev wants to extend single-quoted string literals instead of developing a second syntax.

These people want the proposal to be more specific, but appear to be in favor as long as the missing details don't reveal problems:

  1. Greg Parker (maybe?)
  2. Xiaodi Wu
  3. Gwendel Roué

And these people all seem basically positive, though sometimes with reservations or bikeshedding suggestions:

  1. Me
  2. Tony Allevato
  3. David Hart
  4. Daniel Duan
  5. Ricardo Parada
  6. Kevin Nattinger
  7. Víctor Pimentel Rodríguez
  8. Jarod Long (I think)
  9. Ben Cohen
  10. Thorsten Seitz
  11. Howard Lovatt
  12. T.J. Usiyan

Evolution reviews are not referenda, but I think it's fair to say that the sentiment is mostly positive.

(And if the core team does say they like the approach but want clarifications, I'd be happy to pitch in and earn the co-author credit!)

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I agree the sentiment is entirely positive, but speaking for myself only, I
would not like to see this proposal accepted in its current form.

It needs to be returned for more design and discussion. The idea of
multiline strings is not at issue here, except for a small minority of
community members, but rather the specific design.

Clearly, the proposal authors have a design in mind, but it is not
documented anywhere and cannot (or at least, should not) be accepted for
Swift sight-unseen. My questions, at least, remain almost entirely
unanswered. And they are not even critiques of the design, they are
questions about what the design _is_.

···

On Sun, Apr 9, 2017 at 21:44 Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

On Apr 9, 2017, at 9:29 AM, John Holdsworth via swift-evolution < > swift-evolution@swift.org> wrote:

Perhaps we can find common ground on 1) and 2) and even 3) with a view to
resubmitting if there is time. Seems mostly like we just need to discuss
the delimiter further and decide whether the indent trimming is a bug or a
feature to keep moving and not let another year slip by.

Honestly, I think this is a little premature. If I had to summarize this
thread, I think what I'm seeing is:

1. We wish the proposal were more specific on a few points, like the
de-indenting algorithm.

2. We have lots of different pet syntaxes and preferences.

3. But most of us are still in favor of accepting the proposal.

To back up that last point, I ran through the thread and tried to quickly
figure out what everyone was thinking. These people seem to be opposed to
the proposal:

1. Haravikk doesn't like the de-indenting and seems iffy on multiline
strings in general.
2. David Waite wants a suite of different, orthogonal string literal
features to get enough flexibility.
3. Félix Cloutier is worried that supporting interpolation makes this
feature a powerful footgun.
4. Adrian Zubarev wants to extend single-quoted string literals instead of
developing a second syntax.

These people want the proposal to be more specific, but appear to be in
favor as long as the missing details don't reveal problems:

1. Greg Parker (maybe?)
2. Xiaodi Wu
3. Gwendel Roué

And these people all seem basically positive, though sometimes with
reservations or bikeshedding suggestions:

1. Me
2. Tony Allevato
3. David Hart
4. Daniel Duan
5. Ricardo Parada
6. Kevin Nattinger
7. Víctor Pimentel Rodríguez
8. Jarod Long (I think)
9. Ben Cohen
10. Thorsten Seitz
11. Howard Lovatt
12. T.J. Usiyan

Evolution reviews are not referenda, but I think it's fair to say that the
sentiment is mostly positive.

(And if the core team does say they like the approach but want
clarifications, I'd be happy to pitch in and earn the co-author credit!)

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I think it would be good to clarify that there are two similar things we discuss in this proposal.

This proposal is about a [(multi-line string) literal].

And there is also the [multi-line (string literal)].

These are two different things because (1) does add explicit escaping for some characters, where (2) is the current string literal extended for multilines.

As I already mentioned in my previous post, personally I’m in favor of (2) and the existence of (2) raises the question if we need (1) for implicit escaping at all, which I personally like to compare with laziness.

The existence of (2) is easier from the writers perspective, because if you decide at some point to break up your string into multi lines, the string itself won’t be altered because there is no implicit character added to the string, and indent would work as you’d expect it to work. If one would need more precision there would exist leading and trailing precision characters.

However, I think it might be really good compromise, at least to me, as someone mentioned in this review, that we could add a backslash to the proposed (1) [(multi-line string) literal] to prevent implicit new lines and at the same time add trailing precision.

Final words, if I had to choose between (1) and (2), I’d go with (2), because I don’t like the optimization magic that happens behind the scene and would prefer a concrete model for a string literal.

···

--
Adrian Zubarev
Sent with Airmail

Am 10. April 2017 um 04:44:15, Brent Royal-Gordon via swift-evolution (swift-evolution@swift.org) schrieb:

On Apr 9, 2017, at 9:29 AM, John Holdsworth via swift-evolution <swift-evolution@swift.org> wrote:

Perhaps we can find common ground on 1) and 2) and even 3) with a view to resubmitting if there is time. Seems mostly like we just need to discuss the delimiter further and decide whether the indent trimming is a bug or a feature to keep moving and not let another year slip by.

Honestly, I think this is a little premature. If I had to summarize this thread, I think what I'm seeing is:

1. We wish the proposal were more specific on a few points, like the de-indenting algorithm.

2. We have lots of different pet syntaxes and preferences.

3. But most of us are still in favor of accepting the proposal.

To back up that last point, I ran through the thread and tried to quickly figure out what everyone was thinking. These people seem to be opposed to the proposal:

1. Haravikk doesn't like the de-indenting and seems iffy on multiline strings in general.
2. David Waite wants a suite of different, orthogonal string literal features to get enough flexibility.
3. Félix Cloutier is worried that supporting interpolation makes this feature a powerful footgun.
4. Adrian Zubarev wants to extend single-quoted string literals instead of developing a second syntax.

These people want the proposal to be more specific, but appear to be in favor as long as the missing details don't reveal problems:

1. Greg Parker (maybe?)
2. Xiaodi Wu
3. Gwendel Roué

And these people all seem basically positive, though sometimes with reservations or bikeshedding suggestions:

1. Me
2. Tony Allevato
3. David Hart
4. Daniel Duan
5. Ricardo Parada
6. Kevin Nattinger
7. Víctor Pimentel Rodríguez
8. Jarod Long (I think)
9. Ben Cohen
10. Thorsten Seitz
11. Howard Lovatt
12. T.J. Usiyan

Evolution reviews are not referenda, but I think it's fair to say that the sentiment is mostly positive.

(And if the core team does say they like the approach but want clarifications, I'd be happy to pitch in and earn the co-author credit!)

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

To be totally accurate, I think even amongst the “evolutionaries” there is a broad sets of requirements, and without reducing that set of requirements you won’t get a single approach with enough flexibility to meet everyone’s needs.

If the goal was a minimal set of options, it would probably require us to determine a set of (I’m guessing three) use cases that multi-line string literals are desired for, and attempting syntax for each of those.

An example use which might or might not make the cut:

"When building email responses or a command-line app, I want to make sure I control the formatting of output messages such that they fit properly on an 80-column-width terminal. I am developing in a text editor that displays a ruler at the 80 column mark and uses a monospaced font. I thus want to output text so that it has no prefix character and is unindented, starting at column 0. Ideally, the text can be copied/pasted without syntax-driven changes, so that I can send it around for external content edits.

As I typically write command-line apps as swift files rather than compiled into binaries, it is desirable to have the text self-contained, rather than as an external resource”

(I suspect a similar use case is the motivation behind python’s syntax)

-DW

···

On Apr 9, 2017, at 8:44 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

To back up that last point, I ran through the thread and tried to quickly figure out what everyone was thinking. These people seem to be opposed to the proposal:

  2. David Waite wants a suite of different, orthogonal string literal features to get enough flexibility.

I feel discussion on this proposal is nearing general agreement that while the gist of it
it seems reasonable to many there is also not enough detail in it as submitted.

Brent has person-fully stepped in and rewritten the draft to translate it into a detailed
specification which I’ve updated here:

Perhaps we could use the remaining time on the review to pre-evaluate this draft should
we get the chance to resubmit with modifications to maximise its chances. Key questions:

1) Does Swift need multi-line string literals?
2 ) Is “””long strings””” the way to go subject to a discussion about the precise delimiter
3) Is the “magic" leading whitespace removal a good idea to support indentation.

The main change on the revised proposal from the the original submitted is Thorsten’s
suggestion that Windows line endings in a source file be normalised to \n in the literal.

John

···

On 10 Apr 2017, at 12:09, Thorsten Seitz <tseitz42@icloud.com> wrote:

Am 09.04.2017 um 18:29 schrieb John Holdsworth <mac@johnholdsworth.com <mailto:mac@johnholdsworth.com>>:

Hi, John here, the submitter of the proposal.

First up, I must apologise for putting Brent on the spot when I resubmitted this altered proposal from last year. That was my mistake.

Second up, apologies if the proposal is rather vague on details. In some sense this was intentional as I didn’t want to get too bogged down in specifics (and not at all to do with my limitations as a technical writer!)

I guess we need to build up consensus more slowly by asking the following questions separately so it can be resubmitted rather than giving a binary +/-1 on the proposal as it stands.

1) Does Swift need multi-line string literals?

Yes.

2 ) Is “””long strings””” the way to go subject to a discussion about the precise delimiter

Yes.

3) Is the “magic" leading whitespace removal a good idea to support indentation.

Yes.

4) Does the proposal contain sufficient detail to be discussed/implemented

Thanks for the update! I only have the following issues left:

> All other escapes would be processed as before including interpolation, \n and "
You probably meant \“ instead of " here.

The proposal should state what kind of newline will be used within a multiline string literal. I already proposed that it should be exactly the same as for \n and not the newline character(s) actually used in the file (e.g. LF+CR or LF or CR), to avoid issues when working on different platforms (Windows, Mac, Linux) and/or using Git’s autocrlf feature.

The proposal should give an example how to create a multiline string literal which ends with a newline (AFAIU there should be an empty line before the closing ""“).

-Thorsten

My answer to 1) is obviously yes and I think the discussion has come out about 50/50 so far so lets persevere...

Trying to fie down 2), a “””long string””” or @“long string”@ or _”long string”_ or #”long string”# is a string literal inside a new delimiter. It would be processed exactly as it would a normal string including escapes and interpolation except the string can include unescaped “ or “" and newlines. Also, a \ at the end of the line would mean that particular newline is not included in the string.

For me, the goals of a long string are that it should be able to pasted in (almost) without modification from a text source and that syntax highlighting would work for the widest possible range of text editors and github. “””long string””” is just a trick Python uses to satisfy the second goal (for example this gist <Multi-line String Zoo · GitHub) but highlighting also works for asymmetric delimiters such as @“long string”@ which avoid potential problems with “inversion”. Heredoc or a Swifty #equivalent does not satisfy this second goal at all well and IMHO it should be excluded. It would also be significantly more difficult to integrate into the Swift compiler.

Looking at 3) which is underspecified in the proposal perhaps, I’d consider it a “feature" but I can see it would be too magical for some. To specify it more you could say: if there is only whitespace between the last newline and the end of a multiline literal this whitespace will be stripped from all lines in the literal. If lines do not start with this exact sequence of whitespace a warning is emitted. In addition, if the first character in the literal is a newline it will be removed. This operation could be made explicit e.g. #trimLeft(“”"a literal""")

Perhaps we can find common ground on 1) and 2) and even 3) with a view to resubmitting if there is time. Seems mostly like we just need to discuss the delimiter further and decide whether the indent trimming is a bug or a feature to keep moving and not let another year slip by.

With respect to 4) I’m updating https://github.com/johnno1962a/swift-evolution/blob/master/proposals/0168-multi-line-string-literals.md as the proposal is discussed to fill in some of the gaps & I’ve prepared a toolchain for Swift 3 if you want to try an implementation out <http://johnholdsworth.com/swift-LOCAL-2017-04-09-a-osx.tar.gz&gt;

On 9 Apr 2017, at 15:35, Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

https://github.com/apple/swift-evolution/blob/master/proposals/0168-multi-line-string-literals.md
What is your evaluation of the proposal?

+1

My foremost expectation from multiline string literals is to be able to copy and paste multiline string literals without having to fiddle with escape marks or leading and trailing quotes or continuation characters. This is exactly what the proposal provides and makes it easy to embed SQL, for example (using SQL parameters and not string interpolation of course ;-)

The chosen deindentation rules seem very pragmatic and useful to me.

Additional features for multiline string literals can be added easily later.

I would expect multiline string literals to use the same newline character as "\n“ does, regardless of the newline character actually used in the file.
Furthermore all normal escapes, e.g. \n, \t etc. should probably be available as well.
This should be stated explicitly in the proposal.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes.

Does this proposal fit well with the feel and direction of Swift?

Yes.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

For setting the ground it compares favourably.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Followed most discussions, read the proposal.

-Thorsten

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Okay, so maybe we should not strip the trailing newline.

The xml concatenation example in Brent's revised proposal assumes the literal includes a trailing newline.

Also, if you do this in Swift:

print("Line1\n")
print("Line 2")

It seems to strip trailing newlines. So the above actually outputs this with no empty line in between them:

Line 1
Line 2

Conclusion: I think it is best to include the trailing newline. :-)

···

On Apr 12, 2017, at 1:52 PM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

Actually this would be inconsistent. The lines in between the delimiters should always add an implicit new line if not told otherwise with a backslash. If that wouldn’t be the case than you won’t have any precision in the last line you have there.

Assume you want to concatenate this string:

foo
bar baz
To do so you’d need extra spaces in your " baz" string, because of the inconsistency. However the constant version I’m suggesting is more precise.

let myString = """
    foo
    bar \
    """
     
print(myString + "baz")
The last implicit new line which one would need to escape is a model artifact, but it’s consistent that way.

The extra new line one would ever need to add manually would be only at the top of a multi-line string.

// v1:

"""

foo
bar\
"""

// v2:
"""
\n\
foo
bar\
"""

// v3:
"""
\nfoo
bar\
"""

--
Adrian Zubarev
Sent with Airmail

Am 12. April 2017 um 19:41:14, Ricardo Parada via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

Hi all,

I agree as well, I think we should make optimize for the most common case of multi-line strings. A rule that says strip the first leading newline as well as the trailing newline. So it's almost back to where Brent started with the addition of removing the trailing newline.

Borrowing Adrian's example, I could just have this:

let myReallyLongXMLConstantName = """
    <?xml version="1.0"?>
    <catalog>
        <book id="bk101" empty="">
            <author>John Doe</author>
            <title>XML Developer's Guide</title>
            <genre>Computer</genre>
            <price>44.95</price>
        </book>
    </catalog>
    """
If somebody wants the last line to include a newline at the end, they can just add a \n at the end or an empty line.

So if I do this:
print(myReallyLongXMLConstantName)
print("Text right below")
It would output this:

<?xml version="1.0"?>
<catalog>
     <book id="bk101" empty="">
         <author>John Doe</author>
         <title>XML Developer's Guide</title>
         <genre>Computer</genre>
         <price>44.95</price>
     </book>
</catalog>
Test right below

Without removing the trailing newline then it would print like this:

<?xml version="1.0"?>
<catalog>
     <book id="bk101" empty="">
         <author>John Doe</author>
         <title>XML Developer's Guide</title>
         <genre>Computer</genre>
         <price>44.95</price>
     </book>
</catalog>

Test right below

On Apr 12, 2017, at 12:48 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Agree. I prefer the new rules over the old, but considering common use cases, stripping the leading and trailing newline makes for a more pleasant experience than not stripping either of them.

I think that is generally worth prioritizing over a simpler algorithm or even accommodating more styles. Moreover, a user who wants a trailing or leading newline merely types an extra one if there is newline stripping, so no use cases are made difficult, only a very common one is made more ergonomic.
On Wed, Apr 12, 2017 at 09:52 Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> Am 12.04.2017 um 15:40 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:
>
> Hey folks,
>
>
> We've revised the proposal again. The main difference: You no longer need an initial newline to enable indentation stripping, and stripping no longer removes that newline even if it is present. (Adrian Zubarev and I believe some others argued for this.) We

Hmm, not sure if I like these changes. I expect that almost all strings won't begin with a newline and a majority won’t end with a newline. The new design would require a leading backslash almost all the time and a trailing backslash often, which is ugly:

let mystring = "““\
    text text
    text text\
    "““

-Thorsten

> disagreed with this at first, but it made more sense as we thought about it more. There are a few things we like about it:
>
> 1. The rules and algorithm are simpler.
> 2. It accommodates more coding styles.
> 3. Every non-escaped newline in the literal now creates a corresponding newline in the resulting string.
> 4. it's easy to get the old behavior back by backslashing the leading newline.
>
> Unfortunately, I think this precludes stripping the trailing newline by default, but I think this is ultimately a simpler and better approach than the previous draft.
>
> Other changes:
>
> * We realized we needed to make closing delimiter matching a little more complicated if we wanted to allow one or two adjacent double-quote characters that were part of the literal's contents. Oops.
> * Tabs aren't actually allowed in ordinary string literals, so we now explicitly mention that as a difference between the two types.
> * We wrote some tests for the prototype (though they haven't been updated for this new version yet).
> * There were some other wording changes, particularly in the indentation stripping rationale, but nothing that affects the actual design.
>
> I understand John is working on a new version of his toolchain so people can play with the prototype. We hope to have that ready for you all soon.
>
> Let us know what you think of the revisions!
>
> --
> Brent Royal-Gordon
> Architechies
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Wow, maybe I shouldn't have slept.

Okay, let's deal with trailing newline first. I'm *very* confident that trailing newlines should be kept by default. This opinion comes from lots of practical experience with multiline string features in other languages. In practice, if you're generating files in a line-oriented way, you're usually generating them a line at a time. It's pretty rare that you want to generate half a line and then add more to it in another statement; it's more likely you'll interpolate the data. I'm not saying it doesn't happen, of course, but it happens a lot less often than you would think just sitting by the fire, drinking whiskey and musing over strings.

I know that, if you're pushing for this feature, it's not satisfying to have the answer be "trust me, it's not what you want". But trust me, it's not what you want.

Moving to the other end, I think we could do a leading newline strip *if* we're willing to create multiline and non-multiline modes—that is, newlines are _not allowed at all_ unless the opening delimiter ends its line and the closing delimiter starts its line (modulo indentation). But I'm reluctant to do that because, well, it's weird and complicated. I also get the feeling that, if there's a single-line mode and a multi-line mode, we ought to treat them as truly orthogonal features and allow `"`-delimited strings to use multi-line mode, but I'm really not convinced that's a good idea.

(Note, by the way, that heredocs—a *really* common multiline string design—always strip the leading newline but not the trailing one.)

Adrian cited this example, where I agree that you really don't want the string to be on the same line as the leading delimiter:

  let myReallyLongXMLConstantName = """<?xml version="1.0"?>
                                       <catalog>
                                          <book id="bk101" empty="">
                                             <author>John Doe</author>
                                             <title>XML Developer's Guide</title>
                                             <genre>Computer</genre>
                                             <price>44.95</price>
                                          </book>
                                       </catalog>\
                                       """

But there are lots of places where it works fine. Is there a good reason to force an additional newline in this?

      case .isExprSameType(let from, let to):
        return """checking a value with optional type \(from) against dynamic type \(to) \
                succeeds whenever the value is non-'nil'; did you mean to use '!= nil'?\
                """

I mean, we certainly could, but I'm not convinced we should. At least, not yet.

In any case, trailing newline definitely stays. Leading newline, I'm still thinking about.

As for other things:

* I see zero reason to fiddle with trailing whitespace. If it's there, it might be significant or it might not be. If we strip it by default and we shouldn't, the developer has no way to protect it. Let's trust the developer. (And their tooling—Xcode, I believe Git, and most linters already have trailing whitespace features. We don't need them too.)

* Somebody asked about `"""`-delimited heredocs. I think it's a pretty syntax, but it's not compatible with single-line use of `"""`, and I think that's probably more important. We can always add heredocs in another way if we decide we want them. (I think `#to(END)` is another very Swifty syntax we could use for heredocs--less lightweight, but it gives us a Google-able keyword.)

* Literal spaces and tabs cannot be backslashed. This is really important because, if you see a backslash after the last visible character in a line, you can't tell just by looking whether the next character is a space, tab, or newline. So the solution is, if it's not a newline, it's not valid at all.

I'll respond to Jarod separately.

···

On Apr 12, 2017, at 12:07 PM, John Holdsworth <mac@johnholdsworth.com> wrote:

Finally.. a new Xcode toolchain <http://johnholdsworth.com/swift-LOCAL-2017-04-12-a-osx.tar.gz&gt; is available largely in sync with the proposal as is.
(You need to restart Xcode after selecting the toolchain to restart SourceKit)

I personally am undecided whether to remove the first line if it is empty. The new
rules are more consistent but somehow less practical. A blank initial line is almost
never what a user would want and I would tend towards removing it automatically.
This is almost what a user would it expect it to do.

I’m less sure the same applies to the trailing newline. If this is a syntax for
multi-line strings, I'd argue that they should normally be complete lines -
particularly since the final newline can so easily be escaped.

        let longstring = """\
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod \
            tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, \
            quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\
            """

        print( """\
            Usage: myapp <options>
            
            Run myapp to do mything
            
            Options:
            -myoption - an option
            """ )

(An explicit “\n" in the string should never be stripped btw)

Can we have a straw poll for the three alternatives:

1) Proposal as it stands - no magic removal of leading/training blank lines.
2) Removal of a leading blank line when indent stripping is being applied.
3) Removal of leading blank line and trailing newline when indent stripping is being applied.

My vote is for the pragmatic path: 2)

(The main intent of this revision was actually removing the link between how the
string started and whether indent stripping was applied which was unnecessary.)

On 12 Apr 2017, at 17:48, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Agree. I prefer the new rules over the old, but considering common use cases, stripping the leading and trailing newline makes for a more pleasant experience than not stripping either of them.

I think that is generally worth prioritizing over a simpler algorithm or even accommodating more styles. Moreover, a user who wants a trailing or leading newline merely types an extra one if there is newline stripping, so no use cases are made difficult, only a very common one is made more ergonomic.

--
Brent Royal-Gordon
Architechies

Wow, maybe I shouldn't have slept.

Okay, let's deal with trailing newline first. I'm *very* confident that
trailing newlines should be kept by default. This opinion comes from lots
of practical experience with multiline string features in other languages.
In practice, if you're generating files in a line-oriented way, you're
usually generating them a line at a time. It's pretty rare that you want to
generate half a line and then add more to it in another statement; it's
more likely you'll interpolate the data. I'm not saying it doesn't happen,
of course, but it happens a lot less often than you would think just
sitting by the fire, drinking whiskey and musing over strings.

I know that, if you're pushing for this feature, it's not satisfying to
have the answer be "trust me, it's not what you want". But trust me, it's
not what you want.

This is not a very good argument. If you are generating files in a
line-oriented way, it is the function _emitting_ the string that handles
the line-orientedness, not the string itself. That is the example set by
`print()`:

print("Hello, world!") // Emits "Hello, world!\n"

Once upon a time, if I recall, this function was called `println`, but it
was renamed. This particular example demonstrates why keeping trailing
newlines by default is misguided:

print(
  """
  Hello, world!
  """
)

Under your proposed rules, this emits "Hello, world!\n\n". It is almost
certainly not what you want. Instead, it is a misguided attempt by the
designers of multiline string syntax to do the job that the designers of
`print()` have already accounted for.

If we were to buy your line of reasoning and adapt it for single-line
strings, we would arrive at a rather absurd result. If you're emitting
multiple single-line strings, you almost certainly want a space to separate
them. Again this is exemplified by the behavior of `print()`:

print("Hello", "Brent!")

This emits "Hello Brent!" (and not "HelloBrent!"). But we do not take that
reasoning and demand that "This is my string" end with an default trailing
space, nor do we have `+` concatenate strings by default with a separating
space.

Moving to the other end, I think we could do a leading newline strip *if*

···

On Wed, Apr 12, 2017 at 5:20 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

we're willing to create multiline and non-multiline modes—that is, newlines
are _not allowed at all_ unless the opening delimiter ends its line and the
closing delimiter starts its line (modulo indentation). But I'm reluctant
to do that because, well, it's weird and complicated. I also get the
feeling that, if there's a single-line mode and a multi-line mode, we ought
to treat them as truly orthogonal features and allow `"`-delimited strings
to use multi-line mode, but I'm really not convinced that's a good idea.

(Note, by the way, that heredocs—a *really* common multiline string
design—always strip the leading newline but not the trailing one.)

Adrian cited this example, where I agree that you really don't want the
string to be on the same line as the leading delimiter:

let myReallyLongXMLConstantName = """<?xml version="1.0"?>
                                     <catalog>
                                        <book id="bk101" empty="">
                                           <author>John Doe</author>
                                           <title>XML Developer's
Guide</title>
                                           <genre>Computer</genre>
                                           <price>44.95</price>
                                        </book>
                                     </catalog>\
                                     """

But there are lots of places where it works fine. Is there a good reason
to force an additional newline in this?

case .isExprSameType(let from, let to):
return """checking a value with optional type \(from) against dynamic type
\(to) \
      succeeds whenever the value is non-'nil'; did you mean to use '!=
nil'?\
      """

I mean, we certainly could, but I'm not convinced we should. At least, not
yet.

In any case, trailing newline definitely stays. Leading newline, I'm still
thinking about.

As for other things:

* I see zero reason to fiddle with trailing whitespace. If it's there, it
might be significant or it might not be. If we strip it by default and we
shouldn't, the developer has no way to protect it. Let's trust the
developer. (And their tooling—Xcode, I believe Git, and most linters
already have trailing whitespace features. We don't need them too.)

* Somebody asked about `"""`-delimited heredocs. I think it's a pretty
syntax, but it's not compatible with single-line use of `"""`, and I think
that's probably more important. We can always add heredocs in another way
if we decide we want them. (I think `#to(END)` is another very Swifty
syntax we could use for heredocs--less lightweight, but it gives us a
Google-able keyword.)

* Literal spaces and tabs cannot be backslashed. This is really important
because, if you see a backslash after the last visible character in a line,
you can't tell just by looking whether the next character is a space, tab,
or newline. So the solution is, if it's not a newline, it's not valid at
all.

I'll respond to Jarod separately.

On Apr 12, 2017, at 12:07 PM, John Holdsworth <mac@johnholdsworth.com> > wrote:

Finally.. a new Xcode toolchain
<http://johnholdsworth.com/swift-LOCAL-2017-04-12-a-osx.tar.gz&gt; is
available largely in sync with the proposal as is.
(You need to restart Xcode after selecting the toolchain to restart
SourceKit)

I personally am undecided whether to remove the first line if it is empty.
The new
rules are more consistent but somehow less practical. A blank initial line
is almost
never what a user would want and I would tend towards removing it
automatically.
This is almost what a user would it expect it to do.

I’m less sure the same applies to the trailing newline. If this is a
syntax for
multi-line strings, I'd argue that they should normally be complete lines -
particularly since the final newline can so easily be escaped.

        let longstring = """\
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed
do eiusmod \
            tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
minim veniam, \
            quis nostrud exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat.\
            """

        print( """\
            Usage: myapp <options>

            Run myapp to do mything

            Options:
            -myoption - an option
            """ )

(An explicit “\n" in the string should never be stripped btw)

Can we have a straw poll for the three alternatives:

1) Proposal as it stands - no magic removal of leading/training blank
lines.
2) Removal of a leading blank line when indent stripping is being applied.
3) Removal of leading blank line and trailing newline when indent
stripping is being applied.

My vote is for the pragmatic path: 2)

(The main intent of this revision was actually removing the link between
how the
string started and whether indent stripping was applied which was
unnecessary.)

On 12 Apr 2017, at 17:48, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

Agree. I prefer the new rules over the old, but considering common use
cases, stripping the leading and trailing newline makes for a more pleasant
experience than not stripping either of them.

I think that is generally worth prioritizing over a simpler algorithm or
even accommodating more styles. Moreover, a user who wants a trailing or
leading newline merely types an extra one if there is newline stripping, so
no use cases are made difficult, only a very common one is made more
ergonomic.

--
Brent Royal-Gordon
Architechies

You’re wrong there, Xcode does print this for me:

Line1

Line 2
The new line is there as expected.

···

--
Adrian Zubarev
Sent with Airmail

Am 12. April 2017 um 20:30:00, Ricardo Parada (rparada@mac.com) schrieb:

print("Line1\n")
print("Line 2")

Wow, maybe I shouldn't have slept.

LOL.

My thoughts so far:

• If and only if the opening """ is followed by a newline then strip it.
• Don't strip the trailing newline.

Example:

        print( """
            Usage: myapp <options>
            
            Run myapp to do mything
            
            Options:
            -myoption - an option
            """ )

Additionally, allow the string to start immediately after the opening """. In this scenario the indentation stripping is performed starting with the second line:

  case .isExprSameType(let from, let to):
    return """checking a value with optional type \(from) \
        against dynamic type \(to) \
        succeeds whenever the value is non-'nil'; \
        did you mean to use '!= nil'?\
        """

And definitely something like this too:

let html = """<a href="\(url)" id="link\(i)" class="link">"""

Again, the string literal is allowed to start immediately after the opening """. And No newline stripping is required here because the closing """ is not on a line by itself.

···

On Apr 12, 2017, at 6:20 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Okay, let's deal with trailing newline first. I'm *very* confident that trailing newlines should be kept by default. This opinion comes from lots of practical experience with multiline string features in other languages. In practice, if you're generating files in a line-oriented way, you're usually generating them a line at a time. It's pretty rare that you want to generate half a line and then add more to it in another statement; it's more likely you'll interpolate the data. I'm not saying it doesn't happen, of course, but it happens a lot less often than you would think just sitting by the fire, drinking whiskey and musing over strings.

I know that, if you're pushing for this feature, it's not satisfying to have the answer be "trust me, it's not what you want". But trust me, it's not what you want.

Moving to the other end, I think we could do a leading newline strip *if* we're willing to create multiline and non-multiline modes—that is, newlines are _not allowed at all_ unless the opening delimiter ends its line and the closing delimiter starts its line (modulo indentation). But I'm reluctant to do that because, well, it's weird and complicated. I also get the feeling that, if there's a single-line mode and a multi-line mode, we ought to treat them as truly orthogonal features and allow `"`-delimited strings to use multi-line mode, but I'm really not convinced that's a good idea.

(Note, by the way, that heredocs—a *really* common multiline string design—always strip the leading newline but not the trailing one.)

Adrian cited this example, where I agree that you really don't want the string to be on the same line as the leading delimiter:

  let myReallyLongXMLConstantName = """<?xml version="1.0"?>
                                       <catalog>
                                          <book id="bk101" empty="">
                                             <author>John Doe</author>
                                             <title>XML Developer's Guide</title>
                                             <genre>Computer</genre>
                                             <price>44.95</price>
                                          </book>
                                       </catalog>\
                                       """

But there are lots of places where it works fine. Is there a good reason to force an additional newline in this?

      case .isExprSameType(let from, let to):
        return """checking a value with optional type \(from) against dynamic type \(to) \
                succeeds whenever the value is non-'nil'; did you mean to use '!= nil'?\
                """

I mean, we certainly could, but I'm not convinced we should. At least, not yet.

In any case, trailing newline definitely stays. Leading newline, I'm still thinking about.

As for other things:

* I see zero reason to fiddle with trailing whitespace. If it's there, it might be significant or it might not be. If we strip it by default and we shouldn't, the developer has no way to protect it. Let's trust the developer. (And their tooling—Xcode, I believe Git, and most linters already have trailing whitespace features. We don't need them too.)

* Somebody asked about `"""`-delimited heredocs. I think it's a pretty syntax, but it's not compatible with single-line use of `"""`, and I think that's probably more important. We can always add heredocs in another way if we decide we want them. (I think `#to(END)` is another very Swifty syntax we could use for heredocs--less lightweight, but it gives us a Google-able keyword.)

* Literal spaces and tabs cannot be backslashed. This is really important because, if you see a backslash after the last visible character in a line, you can't tell just by looking whether the next character is a space, tab, or newline. So the solution is, if it's not a newline, it's not valid at all.

I'll respond to Jarod separately.

On Apr 12, 2017, at 12:07 PM, John Holdsworth <mac@johnholdsworth.com <mailto:mac@johnholdsworth.com>> wrote:

Finally.. a new Xcode toolchain <http://johnholdsworth.com/swift-LOCAL-2017-04-12-a-osx.tar.gz&gt; is available largely in sync with the proposal as is.
(You need to restart Xcode after selecting the toolchain to restart SourceKit)

I personally am undecided whether to remove the first line if it is empty. The new
rules are more consistent but somehow less practical. A blank initial line is almost
never what a user would want and I would tend towards removing it automatically.
This is almost what a user would it expect it to do.

I’m less sure the same applies to the trailing newline. If this is a syntax for
multi-line strings, I'd argue that they should normally be complete lines -
particularly since the final newline can so easily be escaped.

        let longstring = """\
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod \
            tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, \
            quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\
            """

        print( """\
            Usage: myapp <options>
            
            Run myapp to do mything
            
            Options:
            -myoption - an option
            """ )

(An explicit “\n" in the string should never be stripped btw)

Can we have a straw poll for the three alternatives:

1) Proposal as it stands - no magic removal of leading/training blank lines.
2) Removal of a leading blank line when indent stripping is being applied.
3) Removal of leading blank line and trailing newline when indent stripping is being applied.

My vote is for the pragmatic path: 2)

(The main intent of this revision was actually removing the link between how the
string started and whether indent stripping was applied which was unnecessary.)

On 12 Apr 2017, at 17:48, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Agree. I prefer the new rules over the old, but considering common use cases, stripping the leading and trailing newline makes for a more pleasant experience than not stripping either of them.

I think that is generally worth prioritizing over a simpler algorithm or even accommodating more styles. Moreover, a user who wants a trailing or leading newline merely types an extra one if there is newline stripping, so no use cases are made difficult, only a very common one is made more ergonomic.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Thank you for the toolchain, I’m currently downloading it to test.

As already mentioned a couple times the rules can be simplified by disallowing text after and before the starting/leading delimiters.

Allow single line tripled string """text"""

Multi-lined text should always be between the delimiter lines. Therefore there is no need for an implicit leading new line or something like """\. The trailing new line is the artifact of the rule #3.

Each line in between the delimiter lines produces an implicit new line if not disabled with an explicit backslash at the end of that line. (Last line produces the mentioned artifact in #2.)

The backslash is also used for trailing precision if needed, however the cost is that the implicit new line is disabled and one would need to write \n\.

The closing delimiter determines the indent for the current multi-line string literal, by calculating its indent-prefix from the start of that line to the delimiter itself.

Each line in between the delimiters is stripped exactly like the calculated indent-prefix in #5. If it’s not possible, because of a mismatch of the spacing characters or a non-spacing character was found before the algorithm finished stripping the current line, a warning is emitted to fix the indent. (This rule covers literally everyones indent preferences, because it should not care if the ident is using spaces only, tabs only or a mix of them.)

#5 and #7 also allows easy leading spacing precision, which was a tedious approach with continuation quotes.

I think I pretty much covered everything here now.

Let me know if I missed something.

···

--
Adrian Zubarev
Sent with Airmail

Am 12. April 2017 um 21:08:19, John Holdsworth via swift-evolution (swift-evolution@swift.org) schrieb:

Finally.. a new Xcode toolchain is available largely in sync with the proposal as is.
(You need to restart Xcode after selecting the toolchain to restart SourceKit)

I personally am undecided whether to remove the first line if it is empty. The new
rules are more consistent but somehow less practical. A blank initial line is almost
never what a user would want and I would tend towards removing it automatically.
This is almost what a user would it expect it to do.

I’m less sure the same applies to the trailing newline. If this is a syntax for
multi-line strings, I'd argue that they should normally be complete lines -
particularly since the final newline can so easily be escaped.

    let longstring = &quot;&quot;&quot;\\
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod \\
        tempor incididunt ut labore et dolore magna aliqua\. Ut enim ad minim veniam, \\
        quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat\.\\
        &quot;&quot;&quot;

    print\( &quot;&quot;&quot;\\
        Usage: myapp &lt;options&gt;
        
        Run myapp to do mything
        
        Options:
        \-myoption \- an option
        &quot;&quot;&quot; \)

(An explicit “\n" in the string should never be stripped btw)

Can we have a straw poll for the three alternatives:

1) Proposal as it stands - no magic removal of leading/training blank lines.
2) Removal of a leading blank line when indent stripping is being applied.
3) Removal of leading blank line and trailing newline when indent stripping is being applied.

My vote is for the pragmatic path: 2)

(The main intent of this revision was actually removing the link between how the
string started and whether indent stripping was applied which was unnecessary.)

On 12 Apr 2017, at 17:48, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

Agree. I prefer the new rules over the old, but considering common use cases, stripping the leading and trailing newline makes for a more pleasant experience than not stripping either of them.

I think that is generally worth prioritizing over a simpler algorithm or even accommodating more styles. Moreover, a user who wants a trailing or leading newline merely types an extra one if there is newline stripping, so no use cases are made difficult, only a very common one is made more ergonomic.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution