Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

String interpolation breaks some custom printf-style functions #3969

Open
jwosty opened this issue Dec 4, 2024 · 2 comments
Open

String interpolation breaks some custom printf-style functions #3969

jwosty opened this issue Dec 4, 2024 · 2 comments

Comments

@jwosty
Copy link
Contributor

jwosty commented Dec 4, 2024

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
Copy link
Contributor Author

jwosty commented Dec 4, 2024

I think this could be fixed by moving the logic for string interpolation processing into the logic for general printf stuff. From the F# user's perspective, interpolation just looks like a special type of printf formatting (i.e. $"%d{x}" becomes "%d%P()").

@jwosty
Copy link
Contributor Author

jwosty commented Dec 4, 2024

Looks like this breaks vanilla kprintf from even working at all:

open System
open FSharp.Core.Printf

try
    let customSprintf (fmt: StringFormat<'T>) : 'T = ksprintf id fmt
    let f (x: int) = customSprintf $"x = %d{x}"
    Console.WriteLine (f 42)
with e ->
    Console.WriteLine ("Error: {0}", e)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant