Description
EDIT: if you want to test this awesome feature yourself (or any other "latest" feature), I've written up (and today, 9 Aug, corrected) a little instruction here: #9893 (comment)
Just some findings I collect here about the upcoming string interpolation feature. Some may be "by design", or may already have been reported, but I offered @cartermp to do some testing, so here it goes ;).
For: #8907 (I used the latest successful build of the PR, of earlier today). Details of feature in RFC: https://github.com/fsharp/fslang-design/blob/master/preview/FS-1001-StringInterpolation.md.
Some additional notes (but not bugs afaict) in this comment by @cartermp: #8907 (comment)
Summary and todo-list
- Half of the expressions are not evaluated [fixed&tested]
- Non-escaped closing curly considered legal [fixed&tested]
- Empty expr between curlies
- Prefix operators don't work on interp strings
- Colors of curlies in tooling
- Expression past closing quote considered part of the expression, making comments part of the interp string
- Wrong error range for missing closing curly, or extra open curly-at-end
- With combinator for
Printf.StringFormat
, errors can be all over the place - Use with literal gives too many errors
- Really weird behavior when (wrongly) using compiler directives + multiline expr in interp strings
- Multiline woes: wrong error-underline calculation
- Multiline woes: coloring is off, part of string default color
- Multiline woes: mysterious errors that are not an error when compiled
- Potential error improvements
Summary of things tested so far (8/8/2020)
- Multiline strings with continuation character (bugs found, see above)
-
#if
statement in multiline expressions just work as expected -
# lineno
statements just work as expected inside interp expr -
#compdirective
, when invalid, wreaks havoc, much more than it used to (see "weird behavior" issue above) - breakpoints in multiline expressions just work and tooltips show correct values
- Coloring and ranges tested, some findings, see above
- Combinators and other functions working on
StringFormat
tested, some findings, see above - Attempted to break the "no nested interp strings unless single-inside-triple", works as expected
- Escape sequences in strings, works as expected
- Non-closing curlies and other bad combinations, works as expected but some issues with tooling (see above)
- Quotations (see @cartermp reference above), works as expected
- Go-to-definition, works
- Nested curlied expressions (records, anonymous records, seq) work as expected (require space between curlies)
- Type inference works as expected (
$"%i{x}
requiresx
to be integer) - Mixing
%
-style and interp-style disallowed, this is as expected - Interaction with
kprintf
works - Interaction with fully-typed
StringFormat
does not work with interp strings, I guess this is as expected:let log msg a = sprintf msg a log "%d" 24 // fine let f x = log "{x}" // error, wrong arity
- Outdent leniency of nested expr: not supported. Perhaps this can be added? (not a bug, though).
Can be fixed by outdenting very far:
// warning on wrong indentation: $"Some interpolated string { let x = 12 let y = 14 x + y }"
$"Some interpolated string { let x = 12 let y = 13 x + y }"
- Use with literals: some issues, see above, but works in general.
- Use of invalid keywords in expr like
module
andtype
give the proper errors.
Half of the expressions are not evaluated fixed/tested
It turns out that there's an issue with the third and subsequent string interpolation section in a composed string, leading to weird results (note how the "." get misplaced). It appears thatonly noOfExpr idiv 2 + 1
get evaluated:
// Missing data, half the expressions are not evaluated, or in the wrong position:
printfn $"""{1}{2}{3}""" // prints "12"
printfn $"""{1}{2}{3}{4}{5}{6}""" // prints "123"
printfn $"""{1}{2}{3}{4}{5}{6}{7}""" // prints "1234"
printfn $"""{$"{1}"}{$"{2}"}{$"{3}"}""" // prints "12"
printfn $"""{1},{2}{3}{4}.{5}""" // prints "1,23.4"
Console.WriteLine $"""{1}{2}{3}""" // prints "12"
Console.WriteLine $"""{1},{2}{3}{4}.{5}""" // prints "1,23.4"
Non-escaped closing curly considered legal fixed/tested
The following came as a surprise to me, but may be based on how C# does things? This came as a surprise, I'd expect orthogonality here:
// inconsistency with escaping: "{" must be escaped as "{{", but "}" can be either literal, or escaped as "}}"
// Is this by accident? It makes more sense to always require escaping "{" AND "}", which means this is invalid:
printfn $"{1}}" // should be invalid, but gives "1}"
printfn $"{1}}}" // is valid and correctly gives "1}"
Empty expr between curlies
I'd prefer this to be legal, esp in light of autogen tools, that now have to put in null
or None
to eval to nothing, or remove the expr part:
let x = $"{}" // error
Prefix operators don't work on interp strings
It's (quite) customary to (re)define unary prefix operators and they used to work "just fine" with strings, but they don't work the same way with interpolated strings, because $
is considered part of the prefix operator. Since it is already forbidden to have an operator contain the dollar sign (except for dollar-only operators), I think this should be legal:
let (!) x = x
printfn !$"{42}" // workaround: use parens
Colors of curlies in tooling
Maybe we can distinguish colors of active {
vs escaped {
? It's pretty hard to notice the difference. Maybe we can make the active ones bright-red or something?
Expression past closing quote considered part of the expression, making comments part of the interp string
This gives "this value is not a function an cannot be applied" error, and wrongly colors the rest of the line:
printfn $"{1}{" // gives wrong error "1}"
This same behavior leads to other weird stuff, where the syntax checker keeps looking beyond the last quote:
Or this one, each line getting red to the end of file:
Wrong error range for missing closing curly, or extra open curly-at-end
Like this:
With combinator for Printf.StringFormat
, errors can be all over the place
(it's actually awesome that this works mostly flawlessly and that I can re-use existing stringformat combinators!)
The following code gets errors all over the place in the whole file:
/// indent each line by 4 spaces
let inline (<|>) a b = Printf.kprintf (sprintf "%s\n %s" a) b
let f x = // wrong error on let, the string is closed, should not happen here
sprintf ""
<|> "Line one is twelve: %d" <| 12
<|> $"Line two is twentyfour: {24}"
<|> $"{1}{2}{3" // error should be only here and coloring should not be gone...
f 1 |> printfn "%s" // wrong error on interp string, this is NOT an interp string and not an error
exit 0
// wrong error at end-of-file for unfinished let, which is untrue, the string has a closing quote
See the 4 (!) errors in this animation, it should ideally be only one on wrong interp string:
Use with literal gives too many errors
I can understand that use with literals is not allowed, not even when it can be assessed that the result of an interp string will be constant. But there are two errors raised, instead of one:
Really weird behavior when (wrongly) using compiler directives + multiline expr in interp strings
I've no idea what happens here, there seems to be nothing consistent. Everything gets green and improper errors pop up. The red #
is as it is now (unexpected symbol in binding), the rest getting green not at all. Notice that this behavior disappears when the interpolated expression part is a single constant.
Repo code:
let x = 1 in printfn "\4\{42}%d{x}" x
/// indent each line by 4 spaces
let inline (<|>) a b = Printf.kprintf (sprintf "%s\n %s" a) b
let log msg a = sprintf msg a
let x = log "%d" 12
//let x = log $"%O{x}" // not possible
let f x =
#nowarn "12" // any wrong compiler directive makes everything green and shows improper errors
sprintf $"ML str {
let x = 12 // replace this multiline with single expr and the green and wrong errors disappear
x }"
Multiline woes, wrong error-underline calculation
let f configFile =
sprintf $"This \
Is \
A \
Multiline string error '{configFile}' %s"
Multiline woes, coloring is off, part of string default color
Multiline woes: mysterious errors that are not an error when compiled
This situation appears to depend on the size of the input (and is not necessarily related to the new feature, I can confirm this was the case previously):
Error is depending on text length in last line, it seems:
Potential error improvements
Here are some areas where we may want to improve error reporting.
-
Something about hinting like "remove whitespace between '{' and % expression" perhaps?
-
Text doesn't match the error, should be something like "Unclosed opening bracket in interpolated string found at position X in string":
-
Not sure how this can be improved, but the user here escaped the curly, but then gets an error that (s)he needs to escape the curly? Maybe we can report all situations where
openingCurlies - closingCurlies <> 0
(excluding escapes) as a variant of "missing opening or closing bracket"? This would solve a range of error issues, I think.
This is cool!
I just have to say I really like it that the result inside {...}
is not just a string, but keeps type information, and that the result can be formatted. So this is entirely expected (and %f
fixes the error):
I'll update this list if I find more stuff (it's getting a bit late over here ;) ).
Anybody else who'd like to try out this new feature and iron out some bugs, here's how to use the absolute latest, I've written some instructions in the PR: #9893 (comment)