> On Apr 12, 2017, at 11:58 AM, Jarod Long via swift-evolution
> <[email protected]> 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
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution