Pre-SIP: Sharp (string) interpolation

Honestly, I do not see a big advantage in this Pre-SIP.
Did I miss something?

Compared to regular string literals/interpolation, you only save up to one char. And also only, if it is not followed by ) or . (or ,, as it is used in big#345,463 above?)
But I think most literals are followed by one of those characters.

I get that it “feels” less like a number if enclosed in doublequotes, but is

ip#192.168.1.0 .connect //OK

really any better than

ip"192.168.1.0".connect

? (maybe that’s more of a syntax highlighting issue?)


And I do not think it is worth all the new ambiguities it introduces.
For another example, I don’t think most people could immediately tell what the following lines should or would do.

Integer.toString(big#543,210) // a
Integer.toString(big#543, 210) // b
Integer.toString(big#543 ,210)  // c
Integer.toString(big#543 , 210) // d

Especially, as the most familiar-looking one, b, is the only one that would not compile (as one literal big#543, is directly followed by another, 210).
In contrast, I think these two are very readable:

Integer.toString(big"543,210") // e
Integer.toString(big"543", 210) // f

(I know, Integer.toString(num, radix) for radix > 36 is doomed anyway, but it’s only about syntax here :wink: )


If you’d introduce a proper end-character such as ; or a second #, I think all these problems go away.

Although that’s more or less just another very similar syntax for what we have with normal string interpolators, it could be interesting for numeric data types, especially if processed in the compiler phase by default.
And it would be much mor capable than FromDigits which just always isn’t enough)

10 Likes

We can choose to exclude commas altogether

The very point is not to require an end marker. When you only need to write a single expression now and then it does not seem significant, but if you have many, then this additional grammar offers a worthy productivity boost, IMO.

@soronpo Some thoughts on your questions above:

  1. I think that it would be best to skip semicolon as it looks more cryptic.
  2. It may seem as an unnecessary restriction to not allow interpolation. But if it gets too complex and provide too many gotchas then skip it. I think I prefer to keep it simple and skip interpolation with $identifier and ${expression}.
  3. Can perhaps FromDigits be evolved to encompass/support/back the new possibilities provided by this proposal?
  4. Yes, I think that would be good to include sharp number literals in the stdlib for the available number types if this proposal gets accepted, as that can serve at least two purposes: a) batteries included, and b) an example of how this language feature can be put into use.
  5. I like the opportunities of making Scala attractive to developers that want richer number literals, including the potential of boosting adoption in scientific computing etc.
    The main drawback I see is that it adds another special language feature that may add “friction to the learning curve” and it kind of looks cryptic to me with that squeezed-in # symbol that sharply sticks in my eyes… (but we might get used to the syntax after a while, esp. if this turns out to be so successful that it sets a new standard for numeric literals that are even “stolen” by other languages :slight_smile: ).

Idea/whim: Perhaps it is possible to make something even more general that can cover/encompass both existing string interpolators and the proposed sharp number literals?

Sounds much better.
Do we choose the same for parenthesis and braces then?
Not being able to write bar(foo#123) or {x => foo#123} could be very confusing.


With other operators and the dot, it could still be confusing, but maybe a little less?

c#1+i     // Complex(1, 1)
c#1 + i   // Complex(2, 0) if val i = 1 somewhere before?
f#2/3     // Fraction(2, 3)
f#2 / 3   // maybe Fraction(2, 1) / 3 ?
base36#foo.bar // bar is the fractional part
base36#foo .bar // bar is a method

Don’t get me wrong here, I’d love to have such a nice syntax for my (wierd) notations and numeric data types! I currently use normal string interpolation for that, as FromDigits does not allow much more than we’re used to.

I am also sure someone can build a parser for that. My fear is only that it may often be harder to distinguish and debug.

1 Like

It has already been established that this will be possible. Either if exclude braces altogether from the pattern or if we just prevent them from being in the pattern suffix.

Here is where it might be a little confusing, but I think we can get used to it and the benefits outweigh the possible confusion. There is no ambiguity here and the rules are clear: whitespace ends the pattern.
I believe that we can live without ability to apply .method without the space, and if someone really hates the space then they can just use regular string interpolation grammar.

1 Like

Please do not go down this path. As much as i love it to have a manner to directly define complex numbers (why is this not in the language anyway?) this is a recipe for big disasters. The language rules are clear, but mistakes are going to be made all over the place.

1 Like

Here is where I disagree, because the grammar and editor coloring will help us avoid these mistakes, the same way it helps us avoid mistakes in strings where we need to put a backslash somehow inside, and the lack of space here is far more trivial than the backslash exception.
With the proper coloring scheme for Sharp Numerics, we can easily see that we added a redundant space.
image

And let me remind you that no space rules already takes place in decimals: 1.243e-3. If we had space inside here we had gotten a different meaning. We’ve just grown accustomed to it.

Good point, this is true

Yeah, but if i place a space anywhere in here, the meaning does not change, it simply results in a compiler error. I would be okay with that.

2 Likes

Wouldn’t it be wise to have a set of premeditated principles and qualities every language modification should be first assessed against?

I remember the ones set by @odersky once here:

  • simplify where possible,
  • eliminate inconsistencies and surprising behaviour,
  • build on strong foundations to ensure the design hangs well together,
  • consolidate language constructs to improve the language’s
    • consistency,
    • safety,
    • ergonomics,
    • and performance.
4 Likes

These are still the yardstick. Conversely, a non-goal for me are language complications that make edge cases or certain DSLs easier to express.

I am rather skeptical whether this PRE-SIP fits in that rationale.

4 Likes

Even if it can bring Scala users from domains like Science that we want and python currently dominates?

What was the rational behind FromDigits? Why existing string interpolation were not satisfactory so it lead to you suggest that feature?

Emphatically, yes. It’s shortsighted to go after user pockets with special features. The last time we did that (and came to regret it) was the introduction of XML literals. Besides, does Python have # literals?

To remove an arbitrary limitation and an inconsistency. We say what an Int and a Float literal is but there’s this arbitrary limit how large it can be. Python does not have that, which makes the language simpler. FromDigits was originally proposed by Guy Steele, so I would be careful not to dismiss it out of hand. I am still very much in favor to adopt it.

Note that the motivations of # interpolators and FromDigits are quite different. FromDigits aims to use the existing Scala literal syntax, just removes a jarring limitation. # interpolators aim to create a new (in my mind, highly complex) syntax for literals.

10 Likes

This Sharp Numerics feature is just another grammar option for string interpolation. That’s it. I don’t understand what is considered complicated about it that is more complicated than the existing grammars of interpolation. If you look closely, it may even be the simplest of the 3 grammars (even after we fix the issues discussed thus far).

XML literals are specific to XML. This is not just a data-science feature, but a general feature that can be good for several use-cases.
Python indeed does not have this # concept, but maybe we can improve Scala enough and make it better, and not just on-par (as far as python devs are concerned).

Just chipping in, I feel like this proposed feature is neat but does not provide sufficient benefit over foo"..." style strings to be worth the complexity. The one character savings simply aren’t enough ROI for me.

Earlier in my Scala career I had a similar idea, and extensivey used 'foo symbols instead of string literals to also save one character. Ammonite-Ops did this extensively, aiming for Bash-like levels of conciseness.

In hindsight, I think it wasn’t really worth it, and nowadays I just use strings and sacrifice the 1 character of verbosity in exchange for significant wins in familiarity for anyone who has used a string in any other programming language. OS-Lib reflects this, looking more like Python than Bash, and is far more popular than Ammonite-Ops ever was

Not all languages make the same tradeoff. Ruby is an example where they go the other way, as is Bash. But my preference is to err on the side of slightly-more-verbosity, and have Scala be more Python-like rather than Ruby-like or Bash-like in its sense of style.

12 Likes

My two cents is that (a) there is a reasonably strong argument to be made that identifier#literal would be nice additional interpolator syntax (I would love to be able to write m#foo to replace the 'foo syntax for symbols) and (b) a weaker argument to be made for extending existing literal syntax to include e.g. 192.100.1.1 or 1.0i2.5 because I believe those introduce no new ambiguities in the grammar. My vote is that they should be considered orthogonally, since (b) could improve FromDigits and (a) could be useful for things other than numeric constants.

In either case, I think the improvement to the language is small. Existing solutions with quotes are pretty good.

2 Likes

Note however that in this case, dot can follow immediately. This is the benefit of built-in vs custom syntax.

1.243e-3.hashCode
// 687901598

clever trick to put the i in the middle! This probably enables adding complex numbers (for all integer and real types) without further hassle and even spaces would not introduce any problems i guess. We could then write:

val z1 = 1 i 4
val z2 = 1e12 i 3e1
val z3 = 1.12 i 3.14
val z4 = 1.1e6 i -21.2e4
val z5 = 1i4 * 3i4

etc. Btw it would also be fine to have it as 1 + 4i but maybe this is less easy to parse?

Anyway, a language that targets itself towards the scientific community should have complex numbers natively build in imho. (Along with a good numerical library for that).

You can already put the i in the middle with infix notation on an extension method on Double. (You just need spaces.)

1 Like

Yes, but this gives problems with the operator precedence rules, for example:

val z = 1 i 2 * 3 i 4

would first multiply 2 and 3. So, possible, yes, but parenthesis all over the place. And it is not the same as a built in number with support for all arithmetic and IDE highlighting etc.

2 Likes

I have another idea. It’s not very convincing (to be honest, even I’m not very impressed about my idea :slight_smile: ), but maybe it makes sense?

The idea is to approach the problem of shortening literals from the other side, i.e. instead of saving on delimiters, we can save on parser name, i.e. make it implicit. Therefore the code:

val xxx = #5m/s^2#

would be expanded into:

val xxx = summon[SharpLiteralParser].apply("5m/s^2")

The problem is how to compute precise types statically, but maybe that could be handled by compiletime.ops on string + match types? Would e.g. IntelliJ and other IDEs be able to compute the precise target types statically?

1 Like