Fable icon indicating copy to clipboard operation
Fable copied to clipboard

String interpolation breaks some custom printf-style functions

Open jwosty opened this issue 1 year ago • 2 comments

Description

Custom printf-style functions have access to the PrintfFormat instance that encodes useful information about the format string itself (both at the type level and at the runtime value level). Fable handles these just fine when it comes to traditional style calls (e.g. myPrintf "Hello, %s" x), but emits code that violates the type signature when used with interpolated strings (e.g. myPrintf $"Hello, %s{x}"). Instead of getting a PrintfFormat object, Fable actually gives you a string masquerading as a PrintfFormat. Many users will never see an issue because they don't need to actually inspect the PrintfFormat, but in the rare case you do, they evaluate to undefined. It would be nice to make Fable behave closer to what the F# compiler does in this case.

I am running into this specifically while adding interpolation support to the Fable portion of FSharp.Logf.

Repro code

open System
open FSharp.Core.Printf

let f (fmt: StringFormat<'T>) : 'T =
    Console.WriteLine fmt.Value
    sprintf fmt

let g x = f "wow %d" x
let h x = f $"wow %d{x}"

g 42
h 42

Expected and actual results

Under .NET, this prints:

fmt.Value = wow %d
fmt.Value = wow %d%P()

Under Fable, this prints:

fmt.Value = wow %d
fmt.Value = undefined

Details

Peeking at the transpiled JS (see here) reveals that for the specific case of interpolated strings, it's being squashed before being passed into the custom printf function, instead of in some inner component.

The specific scenario I'm running into this is while adding string interpolation support to the Fable implementation of my FSharp.Printf library. For some context, it essentially needs to be able to perform a regex replacement on the format string itself (see logf.fs#L277). This code, as written, crashes when used with an interpolated string because format.Value evaluates to undefined. While I can partially work around the issue and make it at least not crash, it's not possible to implement the replacement truly correctly in this scenario without a fix from Fable.

Related information

  • Fable version: 4.24.0
  • Operating system: Windows 11

jwosty avatar Dec 04 '24 19:12 jwosty