fsharp icon indicating copy to clipboard operation
fsharp copied to clipboard

Investigate codegen-based "%A" format string

Open vzarytovskii opened this issue 2 years ago • 2 comments

printf and sprintf rely quite heavily on reflection when providing support for %A item formatting. And it is quite a chunk of FSharp.Core

We would like to remove that dependence to allow, trim to delivery smaller self contained programs and likely to make things perform a bit zoomier.

vzarytovskii avatar Feb 07 '24 13:02 vzarytovskii

To make this more manageable, the feature can be scoped to replacing the sprintfs that currently appear inside generated ToString() methods on DUs and records, when the user has not enabled --reflectionfree. This can be done relatively easily if you give up on carriage returns and spaces. For type DU = | A of B * C just return match du with | DU.A(b, c) -> $A({b}, {c}) and for type Record = { A: U; B: V } just return ${ A = {x.A}; B = {x.B} }.

Why?

Suppose the following things are done, which are high impact and lowish difficulty and so likely to happen before a large feature like this.

  • Emit interpolated strings to a collector (tracked in https://github.com/fsharp/fslang-suggestions/issues/1108) - which is a much smaller task (not necessarily the full suggestion but the collector part).
  • Annotate printf etc. with whatever level of trim-annotation it needs. That will ensure it stays out of user code in the relevant scenarios.
  • Add a print function (https://github.com/fsharp/fslang-suggestions/issues/1092) so those people using printf ${...} can delete the f.

Then there is no reason to use printf etc. directly in user code.

charlesroddie avatar Mar 07 '24 23:03 charlesroddie

To make this more manageable, the feature can be scoped to replacing the sprintfs that currently appear inside generated ToString() methods on DUs and records, when the user has not enabled --reflectionfree. This can be done relatively easily if you give up on carriage returns and spaces. For type DU = | A of B * C just return match du with | DU.A(b, c) -> $A({b}, {c}) and for type Record = { A: U; B: V } just return ${ A = {x.A}; B = {x.B} }.

Why?

Suppose the following things are done, which are high impact and lowish difficulty and so likely to happen before a large feature like this.

  • Emit interpolated strings to a collector (tracked in https://github.com/fsharp/fslang-suggestions/issues/1108) - which is a much smaller task (not necessarily the full suggestion but the collector part).

  • Annotate printf etc. with whatever level of trim-annotation it needs. That will ensure it stays out of user code in the relevant scenarios.

  • Add a print function (https://github.com/fsharp/fslang-suggestions/issues/1092) so those people using printf ${...} can delete the f.

Then there is no reason to use printf etc. directly in user code.

All these are orthogonal to the current issue, we have not been planned any of those for .NET 9 and randomly picking those up will just jeopardise what we want to deliver.

This particular issue is about universal approach of how do we want to compile %A specifier, and do it via code generation instead of reflecting into custom objects.

vzarytovskii avatar Mar 08 '24 03:03 vzarytovskii