Suggestion: Add ways to disable not typed interpolated strings
I propose we have a way to improve interpolated string.
Currently with interpolated strings can be done with or without the type identifier ( %s for example ).
It can be harmful when we do some code refactoring, not-typed interpolated strings can produce unexpected results.
For example :
let hello = "Hello World"
printf $"{hello}" // ✅ Builds fine
printf $"%s{hello}" // ✅ Builds fine, same as `printf "%s" hello`
After some code refactoring we could be in this situation :
type HelloWorld = HelloWorld of string
let hello = HelloWorld "Hello World"
printf $"{hello}" // ❌ Builds fine, prints `HelloWorld "Hello World"`
printf $"%s{hello}" // ✅ Does not build ( Error FS0001 : This expression was expected to have type 'string' but here has type 'HelloWorld' )
I would suggest adding a warning when using not-typed interpolated strings. And an optionnal way to force the usage of typed interpolated string for a project (WarningsAsErrors ?).
Pros and Cons
The advantages of making this adjustment to F# are :
- More safety when using interpolated strings
- Less errors when refactoring code
- Errors detected at compile time
The disadvantages of making this adjustment to F# are :
- It can be a pain to add the type identifier to all interpolated strings
- Warnings can be annoying when using not-typed interpolated strings
Extra information
Estimated cost (XS, S, M, L, XL, XXL): : M (?)
Affidavit (please submit!)
Please tick this by placing a cross in the box:
- [x] This is not a question (e.g. like one you might ask on stackoverflow) and I have searched stackoverflow for discussions of this issue
- [x] I have searched both open and closed suggestions on this site and believe this is not a duplicate
- [x] This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.
Please tick all that apply:
- [x] This is not a breaking change to the F# language design
- [x] I or my company would be willing to help implement and/or test this
For Readers
If you would like to see this issue implemented, please click the :+1: emoji on this issue. These counts are used to generally order the suggestions by engagement.
We could use a GH flag "this would not be needed if F# had Analyzer SDK". I think this suggestion is the kind of coding preference which would very well fit being an own analyzer.
I chatted with @vzarytovskii and we like the idea - whether by analyzer of built-in. So approving this as either an off-by-default warning or a built-in analyzer. It's so simple to implement an off-by-default warning would be just fine I think
Does type identifier affect performance in any way? I could be mistaken, but as far as I remember there was a benchmark where type identifier makes interpolation slower
Yes - more characters that need to be processed at run time.
$"%d{1}"
PrintfModule.PrintFormatToStringThen(new PrintfFormat<string, Unit, string, string, int>("%d%P()", array, null))
$"{1}"
PrintfModule.PrintFormatToStringThen(new PrintfFormat<string, Unit, string, string, int>("%P()", array, null))
It's probably only marginally slower. If performance is a serious concern, I imagine you'd want to avoid sprintf and F# interpolations altogether.
I've decided to verify my memory and run benchmark. Good news is that I was wrong :)
| Method | Mean | Error | StdDev | Gen0 | Allocated |
|----------------- |---------:|--------:|--------:|-------:|----------:|
| Sprintf | 418.2 ns | 5.12 ns | 4.54 ns | 0.0620 | 520 B |
| Interpolate | 282.8 ns | 5.33 ns | 4.99 ns | 0.0467 | 392 B |
| StringFormat | 149.3 ns | 3.00 ns | 3.09 ns | 0.0114 | 96 B |
| InterpolateTyped | 248.5 ns | 4.34 ns | 4.46 ns | 0.0458 | 384 B |
Source code can be found here
It's not just for refactoring. It's also for the pitfall of implicit current culture formatting for IFormattable values.