Alternate proposal for multi-line string literial


(Steve K. Chiu) #1

Hi,

I am new to swift-evolution list, here is my draft proposal for the
multi-line string literal problem.
The idea had been discussed in the list before, but it seems there are no
real solution to many of the string literal problem.
Let's first define what are the problem with string literal:

1. should be able to disable escape char

2. or better, able to replace escape char with user defined char

3. should be able to write multi-line string literal, and is copy-paste
friendly

4. for multi-line string, should be able to remove first and last newline
char, so user can write string in block

5. for multi-line string, should be able to remove leading indent, or
remove all indent

6. for multi-line string, should be able to replace newline with user
defined string ("\r\n", "\r", "\r", or simply " ")

7. should be able to add feature over time, without breaking existing code

My proposal to the above problem is to introduce new 'process instruction'
(not sure how to call it), in the following form:

#string(options) "text"

for example:

#string(escape: nil) "^\d+"

#string(escape: "$", end: "<EOF>") "
   $(username),
   Is it 1358 yet?
<EOF>"

It is possible to add many options list above, and you can add more options
over time without breaking code.

#string(
    escape: Character? = "\\",
    end: String? = nil,
    skipEnclosureNewline: Bool = true,
    skipLeadingIndent: Bool = true,
    skipAllIndent: Bool = false,
    newline: String? = nil
)

for 1. & 2., escape option to replace escape char, pass nil will disable
escape.

for 3., end option for end-of-string mark, pass nil will disable multi-line
processing.

for 4., skipEnclosureNewline will skip newline if it is the first or last
char of the string.

for 5., skipLeadingIndent will skip leading indent, leading indent is the
leading white-spaces of first line of multi-line string.

for 5., skipAllIndent will skip all indent, this will override
skipLeadingIndent.

for 6., newline option to replace newline char in multi-line string, pass
nil will disable the replacement (as-is in the source).

But there are one problem remain, as you can see, the #string with options
will become very long; I don't think it is a pleasure to use such
expression except for one time literal. To fix the problem, I propose yet
another process instruction:

#let #rex = #string(escape: nil)
#let #mail = #string(escape: "$", end: "<EOF>")

Now you can write the string as:

#rex "^\d+"

#mail "
   $(username),
   Is it 1358 yet?
<EOF>"

#let should be able to be used with other # process instruction as well,
for example, #available, among other things.

What do you think?


(Andrey Tarantsov) #2

Feels wrong to me. It's ugly (looks like line noise) and unnecessary (this much flexibility isn't required in practice). Making features more powerful doesn't always make them better.

Some of the use cases you have in mind are probably better handled by macros, whenever they make it into Swift. Macros provide a general and elegant way to handle customizable special cases without introducing an unusual complicated syntax for each of them.

So, nope, sorry, let's stick to making tripe-quote multiline strings as awesome as possible, but not awesomer. :smiley:

A.

···

On Mar 23, 2016, at 10:49 AM, Steve K. Chiu via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

I am new to swift-evolution list, here is my draft proposal for the multi-line string literal problem.
The idea had been discussed in the list before, but it seems there are no real solution to many of the string literal problem.
Let's first define what are the problem with string literal:

1. should be able to disable escape char

2. or better, able to replace escape char with user defined char

3. should be able to write multi-line string literal, and is copy-paste friendly

4. for multi-line string, should be able to remove first and last newline char, so user can write string in block

5. for multi-line string, should be able to remove leading indent, or remove all indent

6. for multi-line string, should be able to replace newline with user defined string ("\r\n", "\r", "\r", or simply " ")

7. should be able to add feature over time, without breaking existing code

My proposal to the above problem is to introduce new 'process instruction' (not sure how to call it), in the following form:

#string(options) "text"

for example:

#string(escape: nil) "^\d+"

#string(escape: "$", end: "<EOF>") "
   $(username),
   Is it 1358 yet?
<EOF>"

It is possible to add many options list above, and you can add more options over time without breaking code.

#string(
    escape: Character? = "\\",
    end: String? = nil,
    skipEnclosureNewline: Bool = true,
    skipLeadingIndent: Bool = true,
    skipAllIndent: Bool = false,
    newline: String? = nil
)

for 1. & 2., escape option to replace escape char, pass nil will disable escape.

for 3., end option for end-of-string mark, pass nil will disable multi-line processing.

for 4., skipEnclosureNewline will skip newline if it is the first or last char of the string.

for 5., skipLeadingIndent will skip leading indent, leading indent is the leading white-spaces of first line of multi-line string.

for 5., skipAllIndent will skip all indent, this will override skipLeadingIndent.

for 6., newline option to replace newline char in multi-line string, pass nil will disable the replacement (as-is in the source).

But there are one problem remain, as you can see, the #string with options will become very long; I don't think it is a pleasure to use such expression except for one time literal. To fix the problem, I propose yet another process instruction:

#let #rex = #string(escape: nil)
#let #mail = #string(escape: "$", end: "<EOF>")

Now you can write the string as:

#rex "^\d+"

#mail "
   $(username),
   Is it 1358 yet?
<EOF>"

#let should be able to be used with other # process instruction as well, for example, #available, among other things.

What do you think?

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


(Patrick Gili) #3

I'm more in favor of Perl6 stye string literals, in general.

-Patrick

···

On Mar 23, 2016, at 12:49 AM, Steve K. Chiu via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

I am new to swift-evolution list, here is my draft proposal for the multi-line string literal problem.
The idea had been discussed in the list before, but it seems there are no real solution to many of the string literal problem.
Let's first define what are the problem with string literal:

1. should be able to disable escape char

2. or better, able to replace escape char with user defined char

3. should be able to write multi-line string literal, and is copy-paste friendly

4. for multi-line string, should be able to remove first and last newline char, so user can write string in block

5. for multi-line string, should be able to remove leading indent, or remove all indent

6. for multi-line string, should be able to replace newline with user defined string ("\r\n", "\r", "\r", or simply " ")

7. should be able to add feature over time, without breaking existing code

My proposal to the above problem is to introduce new 'process instruction' (not sure how to call it), in the following form:

#string(options) "text"

for example:

#string(escape: nil) "^\d+"

#string(escape: "$", end: "<EOF>") "
   $(username),
   Is it 1358 yet?
<EOF>"

It is possible to add many options list above, and you can add more options over time without breaking code.

#string(
    escape: Character? = "\\",
    end: String? = nil,
    skipEnclosureNewline: Bool = true,
    skipLeadingIndent: Bool = true,
    skipAllIndent: Bool = false,
    newline: String? = nil
)

for 1. & 2., escape option to replace escape char, pass nil will disable escape.

for 3., end option for end-of-string mark, pass nil will disable multi-line processing.

for 4., skipEnclosureNewline will skip newline if it is the first or last char of the string.

for 5., skipLeadingIndent will skip leading indent, leading indent is the leading white-spaces of first line of multi-line string.

for 5., skipAllIndent will skip all indent, this will override skipLeadingIndent.

for 6., newline option to replace newline char in multi-line string, pass nil will disable the replacement (as-is in the source).

But there are one problem remain, as you can see, the #string with options will become very long; I don't think it is a pleasure to use such expression except for one time literal. To fix the problem, I propose yet another process instruction:

#let #rex = #string(escape: nil)
#let #mail = #string(escape: "$", end: "<EOF>")

Now you can write the string as:

#rex "^\d+"

#mail "
   $(username),
   Is it 1358 yet?
<EOF>"

#let should be able to be used with other # process instruction as well, for example, #available, among other things.

What do you think?

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


(Steve K. Chiu) #4

Perl are using heredoc notation, for example:

my $message = <<'END_MESSAGE';
void main() {
     printf("%s\n", "hello world");
}
END_MESSAGE

or Perl6

https://doc.perl6.org/language/quoting

Lua's multi-line raw string look like this:
(you can add extra = inside [[ and ]], in case the text contains [[ or ]])

local message = [[
void main() {
     printf("%s\n", "hello world");
}
]]

or with ]] in text:

local message = [===[
void main() {
     printf("[[%s]]\n", "hello world");
}
]===]

And C++11 raw string has the same mechanism as Lua (but does not remove
first/last newline)

auto message = R"(void main() {
     printf("[[%s]]\n", "hello world");
})";

or with )" in text:

auto message = R"===(void main() {
     printf("(%s)", "hello world");
})===";

I think adding extra tag inside the string deliminator is actually a good
idea, I would like to revise the proposal as:

#string(options) ``text``

And then you can add extra tag inside back-quote, just like Lua and C++11
raw strings, so the shortest version would look like:

``^\d+``

It is raw string (escape is disabled), useful for regexp immediately, and
if you need to write `` inside the string:

let message =
`==`
void main() {
     printf("``(%s)``", "hello world");
}
`==`

or with options:

let message =
#string(escape: "$")
``
void main() {
     printf("(%s)", "$(value)");
}
``

···

2016-03-24 19:46 GMT+08:00 Step C <schristopher@bignerdranch.com>:

Happy to see someone with a proposal on this!

> On Mar 23, 2016, at 12:49 AM, Steve K. Chiu via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Hi,
>
> I am new to swift-evolution list, here is my draft proposal for the
multi-line string literal problem.
> The idea had been discussed in the list before, but it seems there are
no real solution to many of the string literal problem.
> Let's first define what are the problem with string literal:
>
> 1. should be able to disable escape char
>
> 2. or better, able to replace escape char with user defined char
Yes!
>
> 3. should be able to write multi-line string literal, and is copy-paste
friendly
>
> 4. for multi-line string, should be able to remove first and last
newline char, so user can write string in block
>
> 5. for multi-line string, should be able to remove leading indent, or
remove all indent
>
> 6. for multi-line string, should be able to replace newline with user
defined string ("\r\n", "\r", "\r", or simply " ")
>
> 7. should be able to add feature over time, without breaking existing
code
>
> My proposal to the above problem is to introduce new 'process
instruction' (not sure how to call it), in the following form:
>
> #string(options) "text"

Building on our meaning for # as compile-time magic. Ok.
>
> for example:
>
> #string(escape: nil) "^\d+"
>
> #string(escape: "$", end: "<EOF>") "
> $(username),
> Is it 1358 yet?
> <EOF>"
>
> It is possible to add many options list above, and you can add more
options over time without breaking code.
>
> #string(
> escape: Character? = "\\",
> end: String? = nil,
> skipEnclosureNewline: Bool = true,
> skipLeadingIndent: Bool = true,
> skipAllIndent: Bool = false,
> newline: String? = nil
> )
>
> for 1. & 2., escape option to replace escape char, pass nil will disable
escape.
>
> for 3., end option for end-of-string mark, pass nil will disable
multi-line processing.
>
> for 4., skipEnclosureNewline will skip newline if it is the first or
last char of the string.
>
> for 5., skipLeadingIndent will skip leading indent, leading indent is
the leading white-spaces of first line of multi-line string.
>
> for 5., skipAllIndent will skip all indent, this will override
skipLeadingIndent.
>
> for 6., newline option to replace newline char in multi-line string,
pass nil will disable the replacement (as-is in the source).
>
> But there are one problem remain, as you can see, the #string with
options will become very long; I don't think it is a pleasure to use such
expression except for one time literal. To fix the problem, I propose yet
another process instruction:
>
> #let #rex = #string(escape: nil)
> #let #mail = #string(escape: "$", end: "<EOF>")
>
> Now you can write the string as:
>
> #rex "^\d+"
>
> #mail "
> $(username),
> Is it 1358 yet?
> <EOF>"
>
> #let should be able to be used with other # process instruction as well,
for example, #available, among other things.
>
> What do you think?

I'd like to see a comparison to how other languages, particularly Perl,
handle this. I like Chris Lattner's vision for this to be the power-user
version of string literals. As you identified, a problem with this solution
is verbosity / noisiness. I don't like the #let #token syntax on first
blush - seems macro-ish to me. But I don't have a better suggestion right
now. Hmmm.

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


(Patrick Gili) #5

https://doc.perl6.org/language/quoting

···

On Mar 24, 2016, at 9:23 AM, Patrick Gili <gili.patrick.r@gili-labs.com> wrote:

I'm more in favor of Perl6 stye string literals, in general.

-Patrick

On Mar 23, 2016, at 12:49 AM, Steve K. Chiu via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

I am new to swift-evolution list, here is my draft proposal for the multi-line string literal problem.
The idea had been discussed in the list before, but it seems there are no real solution to many of the string literal problem.
Let's first define what are the problem with string literal:

1. should be able to disable escape char

2. or better, able to replace escape char with user defined char

3. should be able to write multi-line string literal, and is copy-paste friendly

4. for multi-line string, should be able to remove first and last newline char, so user can write string in block

5. for multi-line string, should be able to remove leading indent, or remove all indent

6. for multi-line string, should be able to replace newline with user defined string ("\r\n", "\r", "\r", or simply " ")

7. should be able to add feature over time, without breaking existing code

My proposal to the above problem is to introduce new 'process instruction' (not sure how to call it), in the following form:

#string(options) "text"

for example:

#string(escape: nil) "^\d+"

#string(escape: "$", end: "<EOF>") "
  $(username),
  Is it 1358 yet?
<EOF>"

It is possible to add many options list above, and you can add more options over time without breaking code.

#string(
   escape: Character? = "\\",
   end: String? = nil,
   skipEnclosureNewline: Bool = true,
   skipLeadingIndent: Bool = true,
   skipAllIndent: Bool = false,
   newline: String? = nil
)

for 1. & 2., escape option to replace escape char, pass nil will disable escape.

for 3., end option for end-of-string mark, pass nil will disable multi-line processing.

for 4., skipEnclosureNewline will skip newline if it is the first or last char of the string.

for 5., skipLeadingIndent will skip leading indent, leading indent is the leading white-spaces of first line of multi-line string.

for 5., skipAllIndent will skip all indent, this will override skipLeadingIndent.

for 6., newline option to replace newline char in multi-line string, pass nil will disable the replacement (as-is in the source).

But there are one problem remain, as you can see, the #string with options will become very long; I don't think it is a pleasure to use such expression except for one time literal. To fix the problem, I propose yet another process instruction:

#let #rex = #string(escape: nil)
#let #mail = #string(escape: "$", end: "<EOF>")

Now you can write the string as:

#rex "^\d+"

#mail "
  $(username),
  Is it 1358 yet?
<EOF>"

#let should be able to be used with other # process instruction as well, for example, #available, among other things.

What do you think?

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


(Steve K. Chiu) #6

this much flexibility isn't required in practice

I think the syntax may be improved in some way, but I don't agree with this.
The options is needed if you ever try to use swift as source code (or html)
generator, take skipLeadingIndent option for example, it will remove
leading indent for each line in the text block, without it you have to put
each line at line start, rather than indent nicely with your code.

I am not sure about tripe-quote multiline (is there any link to the
proposal?), but I suppose you take hint from markdown, a short example may
look like this:

class Test {
    func output(resp: HtmlResponse) {
        resp.write(```
<HTML>
<BODY>
    ooxx
</BODY>
</HTML>
```)
    }
}

And in my proposal, :

#let #html = #string(end:"--", skipLeadingIndent: true)

class Test {
    func output(resp: HtmlResponse) {
        resp.write(#html "
            <HTML>
            <BODY>
                ooxx
            </BODY>
            </HTML>
            --")
    }
}

I don't think it look ugly in anyway.

···

2016-03-24 5:13 GMT+08:00 Andrey Tarantsov <andrey@tarantsov.com>:

Feels wrong to me. It's ugly (looks like line noise) and unnecessary (this
much flexibility isn't required in practice). Making features more powerful
doesn't always make them better.

Some of the use cases you have in mind are probably better handled by
macros, whenever they make it into Swift. Macros provide a general and
elegant way to handle customizable special cases without introducing an
unusual complicated syntax for each of them.

So, nope, sorry, let's stick to making tripe-quote multiline strings as
awesome as possible, but not awesomer. :smiley:

A.

> On Mar 23, 2016, at 10:49 AM, Steve K. Chiu via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Hi,
>
> I am new to swift-evolution list, here is my draft proposal for the
multi-line string literal problem.
> The idea had been discussed in the list before, but it seems there are
no real solution to many of the string literal problem.
> Let's first define what are the problem with string literal:
>
> 1. should be able to disable escape char
>
> 2. or better, able to replace escape char with user defined char
>
> 3. should be able to write multi-line string literal, and is copy-paste
friendly
>
> 4. for multi-line string, should be able to remove first and last
newline char, so user can write string in block
>
> 5. for multi-line string, should be able to remove leading indent, or
remove all indent
>
> 6. for multi-line string, should be able to replace newline with user
defined string ("\r\n", "\r", "\r", or simply " ")
>
> 7. should be able to add feature over time, without breaking existing
code
>
> My proposal to the above problem is to introduce new 'process
instruction' (not sure how to call it), in the following form:
>
> #string(options) "text"
>
> for example:
>
> #string(escape: nil) "^\d+"
>
> #string(escape: "$", end: "<EOF>") "
> $(username),
> Is it 1358 yet?
> <EOF>"
>
> It is possible to add many options list above, and you can add more
options over time without breaking code.
>
> #string(
> escape: Character? = "\\",
> end: String? = nil,
> skipEnclosureNewline: Bool = true,
> skipLeadingIndent: Bool = true,
> skipAllIndent: Bool = false,
> newline: String? = nil
> )
>
> for 1. & 2., escape option to replace escape char, pass nil will disable
escape.
>
> for 3., end option for end-of-string mark, pass nil will disable
multi-line processing.
>
> for 4., skipEnclosureNewline will skip newline if it is the first or
last char of the string.
>
> for 5., skipLeadingIndent will skip leading indent, leading indent is
the leading white-spaces of first line of multi-line string.
>
> for 5., skipAllIndent will skip all indent, this will override
skipLeadingIndent.
>
> for 6., newline option to replace newline char in multi-line string,
pass nil will disable the replacement (as-is in the source).
>
> But there are one problem remain, as you can see, the #string with
options will become very long; I don't think it is a pleasure to use such
expression except for one time literal. To fix the problem, I propose yet
another process instruction:
>
> #let #rex = #string(escape: nil)
> #let #mail = #string(escape: "$", end: "<EOF>")
>
> Now you can write the string as:
>
> #rex "^\d+"
>
> #mail "
> $(username),
> Is it 1358 yet?
> <EOF>"
>
> #let should be able to be used with other # process instruction as well,
for example, #available, among other things.
>
> What do you think?
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution


(Steve K. Chiu) #7

And it seems no one like the #let idea, then how about just reuse
typealias, or simple let?

typealias #text = #string(escape: "$")

or

let #text = #string(escape: "$")

···

2016-03-24 21:43 GMT+08:00 Steve K. Chiu <steve.k.chiu@gmail.com>:

Perl are using heredoc notation, for example:

my $message = <<'END_MESSAGE';
void main() {
     printf("%s\n", "hello world");
}
END_MESSAGE

or Perl6

https://doc.perl6.org/language/quoting

Lua's multi-line raw string look like this:
(you can add extra = inside [[ and ]], in case the text contains [[ or ]])

local message = [[
void main() {
     printf("%s\n", "hello world");
}
]]

or with ]] in text:

local message = [===[
void main() {
     printf("[[%s]]\n", "hello world");
}
]===]

And C++11 raw string has the same mechanism as Lua (but does not remove
first/last newline)

auto message = R"(void main() {
     printf("[[%s]]\n", "hello world");
})";

or with )" in text:

auto message = R"===(void main() {
     printf("(%s)", "hello world");
})===";

I think adding extra tag inside the string deliminator is actually a good
idea, I would like to revise the proposal as:

#string(options) ``text``

And then you can add extra tag inside back-quote, just like Lua and C++11
raw strings, so the shortest version would look like:

``^\d+``

It is raw string (escape is disabled), useful for regexp immediately, and
if you need to write `` inside the string:

let message =
`==`
void main() {
     printf("``(%s)``", "hello world");
}
`==`

or with options:

let message =
#string(escape: "$")
``
void main() {
     printf("(%s)", "$(value)");
}
``

2016-03-24 19:46 GMT+08:00 Step C <schristopher@bignerdranch.com>:

Happy to see someone with a proposal on this!

> On Mar 23, 2016, at 12:49 AM, Steve K. Chiu via swift-evolution < >> swift-evolution@swift.org> wrote:
>
> Hi,
>
> I am new to swift-evolution list, here is my draft proposal for the
multi-line string literal problem.
> The idea had been discussed in the list before, but it seems there are
no real solution to many of the string literal problem.
> Let's first define what are the problem with string literal:
>
> 1. should be able to disable escape char
>
> 2. or better, able to replace escape char with user defined char
Yes!
>
> 3. should be able to write multi-line string literal, and is copy-paste
friendly
>
> 4. for multi-line string, should be able to remove first and last
newline char, so user can write string in block
>
> 5. for multi-line string, should be able to remove leading indent, or
remove all indent
>
> 6. for multi-line string, should be able to replace newline with user
defined string ("\r\n", "\r", "\r", or simply " ")
>
> 7. should be able to add feature over time, without breaking existing
code
>
> My proposal to the above problem is to introduce new 'process
instruction' (not sure how to call it), in the following form:
>
> #string(options) "text"

Building on our meaning for # as compile-time magic. Ok.
>
> for example:
>
> #string(escape: nil) "^\d+"
>
> #string(escape: "$", end: "<EOF>") "
> $(username),
> Is it 1358 yet?
> <EOF>"
>
> It is possible to add many options list above, and you can add more
options over time without breaking code.
>
> #string(
> escape: Character? = "\\",
> end: String? = nil,
> skipEnclosureNewline: Bool = true,
> skipLeadingIndent: Bool = true,
> skipAllIndent: Bool = false,
> newline: String? = nil
> )
>
> for 1. & 2., escape option to replace escape char, pass nil will
disable escape.
>
> for 3., end option for end-of-string mark, pass nil will disable
multi-line processing.
>
> for 4., skipEnclosureNewline will skip newline if it is the first or
last char of the string.
>
> for 5., skipLeadingIndent will skip leading indent, leading indent is
the leading white-spaces of first line of multi-line string.
>
> for 5., skipAllIndent will skip all indent, this will override
skipLeadingIndent.
>
> for 6., newline option to replace newline char in multi-line string,
pass nil will disable the replacement (as-is in the source).
>
> But there are one problem remain, as you can see, the #string with
options will become very long; I don't think it is a pleasure to use such
expression except for one time literal. To fix the problem, I propose yet
another process instruction:
>
> #let #rex = #string(escape: nil)
> #let #mail = #string(escape: "$", end: "<EOF>")
>
> Now you can write the string as:
>
> #rex "^\d+"
>
> #mail "
> $(username),
> Is it 1358 yet?
> <EOF>"
>
> #let should be able to be used with other # process instruction as
well, for example, #available, among other things.
>
> What do you think?

I'd like to see a comparison to how other languages, particularly Perl,
handle this. I like Chris Lattner's vision for this to be the power-user
version of string literals. As you identified, a problem with this solution
is verbosity / noisiness. I don't like the #let #token syntax on first
blush - seems macro-ish to me. But I don't have a better suggestion right
now. Hmmm.

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