Multiline string literals: can we get rid of the need for stripMargin?

Please do not forget this use case:

"""
    one
    "two"
    three
  """

I really like this about """ now. Same example with escaping single quotes:

"
    one
   \"two\"
    three
  "
4 Likes

``` would allow preserving the current semantics of """ and ", and avoid the need to quote embedded single quotes:

```
    one
    "two"
    three
  ```

There are other alternatives for creating a fenced block that could be worth exploring, like the heredoc:

val multiLineString = <<<EOF
    one
    "two"
    three
  EOF

println(<<<EOF
    one
    "two"
    three
  EOF
)

Yes, but the problem is that we cannot change the semantics of """ and we will not introduce a third quote such as ```. So that leaves us with multiline " which is still available. But having to escape quotes at start of lines is not nice, I agree.

One solution to this would be to use the existing stripMargin. I.e. you could write the example like this:

  "
  |one
  |"two"
  |three
  ".stripMargin

That’s just the old stripMargin we have, used in the new context.

heredoc idea looks interesting and could be changed a bit to reuse " syntax, i.e.

val x = "EOF
    one
    "two"
    """three"""
EOF
1 Like

A conservative approach would be to have a new stripping interpolator that:

val x = str”foo
           |bar
           |baz”

And deprecate the old one.

Using a custom interpolator is not good enough as I mentioned before: Multiline string literals: can we get rid of the need for stripMargin?

Hear, hear! or rather Here, here! for heredoc.

REPL lets you specify a margin character, by analogy to detabbing <<- or <<~ style.

scala> :paste <| EOF
// Entering paste mode (EOF to finish)

    |class C { def f = 42 }
EOF

// Exiting paste mode, now interpreting.

defined class C

Probably there would be more demand for this sort of thing, and uniformity across snippets, scripts, and so-called normal code, if Scala were used more in anger for “scaled” development. Right now, as per the other thread, my main snippet doesn’t scale from REPL to @main.

As for Scaladoc, we’d need to standardize the spelling of hereDoc. I’d be inclined to pronounce it like “heretic”, as it is a syntactic tic.

It’s clear heredoc syntax isn’t used in REPL because no one complains about it. I’d expect the first case to detect the desired indent.

scala> :pa <-
// Entering paste mode (ctrl-D to finish)

  val s = """
    hi
    world
  """

// Exiting paste mode, now interpreting.

s: String =
"
hi
world
"

scala> :pa <*
// Entering paste mode (ctrl-D to finish)

val s = """
  *  hi
  *  world
"""

// Exiting paste mode, now interpreting.

s: String =
"
  hi
  world
"

scala>

It’s encouraging that REPL already prints multiline singly-quoted strings.

The current limitation on interpolators excludes val s = <<EOF"my text... in some shoehorning of syntax. Reminder that the result can also take parameters, such as

val s = sm" * strip comment asterisk"("* ")

I haven’t read through all the comments, just wanted to mention that groovy has a stripIndent function. I did not have a look at their algorithm but maybe it is good enough (never had problems at least):

I see that you linked to a post that said it wasn’t optimal because you felt the default could do it, but I don’t see that it “is not good enough”. It is a fact of life to deal with deprecations. Adding a new interpolator and deprecating the old one seems “good enough” to me. Adding more syntax is a real cost.

The problem is that we want this behavior for all interpolators, not just one specific interpolator.

Did something happen here?

I have to say that it’s annoying to need the complexity of “stripMargin” again after using some Java lately…

Scala has significant whitespace now by default. Just that it does not work for multi-line Strings… That’s inconsequential. And it’s just outright annoying after you got used to significant whitespace. Also it’s kind of frightening to have to say that Java is more ergonomic (in this regard). That’s completely backwards from the perspective of a Scala developer.

I think the implementation complexity is not a valid argument as the implementation complexity for significant whitespace is anyway much bigger, and making it work for multi-line strings also makes no big difference anymore.

(The other thing is, more intelligent multi-line strings in general would be good. I think one should just copy what C# does, as it’s imho currently the best solution. But that’s a different topic, I guess.)

1 Like

Just random thoughts:

What if we used ":\n to indicate a new text block?

It would be somehow “consistent” with the other uses of colon-newline.

Such text blocks would not need any closing symbol, as this is how indentation based blocks work in general.

Also this would open up the possibility to do what C# does: You could add arbitrary many double-quote signs in front of the colon to make this escape (this amount - 1) double-quotes in the text block.

Forget that part. This would work even better than C# as you don’t have to treat double-quotes anyhow special inside the text block! Only ending the block would terminate the string. So a simple ":\n is sufficient.

val wipThesis = ":
   Lorem ipsum dolor sit amet…
   "Look, I can use doube-quotes! Even two or more of them: """
   Quotes should not end the text block!
   Only an "unindent" would do.

val importantNumber = 42

What do you think? Is this doable?

1 Like

That’s interesting, but I wonder if it can work with string interpolation without any ambiguities

If we’re going with this, then we should not use ", but something else:

val wipThesis = string:
   Lorem ipsum dolor sit amet…
   "Look, I can use doube-quotes! Even two or more of them: """
   Quotes should not end the text block!
   Only an "unindent" would do.

val importantNumber = 42

Since quotes don’t end it, it feels weird for them to open it
And since it’s a multiline string, a few extra characters to type an identifier is not really a problem

Using something non-symbolic has the disadvantage of being less elegant for string interpolators:

val wipThesis = s string:
   Lorem ipsum dolor sit amet…

val wipThesis2 = raw string:
   Lorem ipsum dolor sit amet…

val importantNumber = 42

As for interpolation itself, I don’t think it would be a problem, since we always know where we are using the number of braces

It also opens the door for things like this, which I find very cool:


val myObject = json string:
  {"menu": {
    "id": "file",
    "value": "File",
    "popup": {
      "menuitem": [
        {"value": "New", "onclick": "CreateNewDoc()"},
        {"value": "Open", "onclick": "OpenDoc()"},
        {"value": "Close", "onclick": "CloseDoc()"}
      ]
    }
  }}

(Example json taken from JSON Example)

3 Likes

Could I also do

val myJson = json:
  {"menu": {
    "id": "file",
    "value": "File",
    "popup": {
      "menuitem": [
        {"value": "New", "onclick": "CreateNewDoc()"},
        {"value": "Open", "onclick": "OpenDoc()"},
        {"value": "Close", "onclick": "CloseDoc()"}
      ]
    }
  }}

? (and with my own customizations similar to String Interpolation | Scala 3 — Book | Scala Documentation ?)

That you could not, as it would desugar to:

val myJson = json.apply{
  {"menu": {
    "id": "file",
    "value": "File",
    "popup": {
      "menuitem": [
        {"value": "New", "onclick": "CreateNewDoc()"},
        {"value": "Open", "onclick": "OpenDoc()"},
        {"value": "Close", "onclick": "CloseDoc()"}
      ]
    }
  }}
}

Maybe we can do something clever to disambiguate, but I’m not sure we should

I disagree.

If it’s a string literal it should at least resemble a string literal.

Adding random, special cased syntax looks odd. Especially if this syntax does already mean something completely different.

There is no reason to “end” some symbol. A symbol is just a symbol. (You would also not propose to “open” a semicolon delimited syntax snippet with a semicolon, I guess.)

(BTW. You also didn’t use a “.” to end the second cited sentence. So there is clearly no issue with leaving out an “end” symbol, right? :wink:)

Actually I was already thinking this concept could be extended further to make the language more regular: I would actually like to have also (:\n so you could leave out the

      )
    )
  )
)

nonsense we have still in the language despite we got rid of the same nonsense with curly braces.

The use of :\n is still completely random currently. Imho it should be governed by some universal rule and not force one to remember arbitrary syntax, like it’s currently.

The obvious rule for :
      It opens a block.

But than this needs to work “everywhere”. Otherwise it’s not a useful rule.

What ambiguities?

No symbol inside the block has special meaning regarding the block. Only unindent has. That’s the key!

If you open another block inside the text block using interpolation this new block would need to be indented of course. Again nothing besides an unindent could close it.

I don’t see how you could construct anything ambiguous. Using proper blocks is actually even more robust than using some special symbol combinations, which could be also valid content of a block, and than you need some mechanisms for escapes (and then you need a mechanisms for escapes of escapes, etc.). Just using indentation would make everything much simpler and completely rule out the possibility of ambiguities as I see it currently. (Besides someone adds a “negative tab” to ASCII, or so…)

I think we simply disagree on this point, especially with parens, it’s a symbol that asks to be closed, so much so most editors are written around it !

It was for example a pain to toy around with Toi as it uses syntax like ( [ ]
Spec: Toi - Esolang
my project: GitHub - Sporarum/Toi-Interpreter: An interpreter for the Toi language written in scala, with some extra syntactic sugar, like tuples and lists

I do agree with the sentiment, but this is not really the right thread to discuss it

2 Likes

You may find that people will be more open to your thoughts when you stop calling all of their ideas random, special cased, arbitrary and similar things.

3 Likes