New syntax for string interpolation in F#

Adam Boniecki

We are excited to announce a new F# syntax feature that is now available in preview, designed to make working with interpolated strings easier than ever before. This feature is modeled after how interpolation works in C#’s raw strings, but maintains backwards compatibility with F#’s triple quoted strings.

Interpolated strings are a very convenient way for developers to embed F# expressions into string literals. However, one scenario where working with interpolated strings can become cumbersome is dealing with text that contains many curly braces. That’s where the new F# interpolation syntax comes in.

Overview

One example where this new syntax can be particularly useful is when working with CSS literals in a front-end F# application, such as with Fable. With the new syntax, you can write your CSS without worrying about escaping curly braces, allowing you to focus on the interpolation expressions themselves. Here’s an example:

Code example showing new interpolated string syntax on a CSS content

Code
let transitionMs = 50
let css = $$"""
:host {
    transition-duration: {{transitionMs}}ms;
    border: 2px solid lightgray;
    border-radius: 10px;
    margin: 5px 0;
}

:host.transition-enter {
    opacity: 0;
    transform: scale(2);
}
:host.transition-leave {
    opacity: 0;
    transform: scale(0.1);
}
.is-clickable {
    user-select: none;
}
"""

You don’t have to comb through all your string literals looking for {, } or % characters to escape, you can just write (or copy-paste) them as you normally would. And the end result looks even more like CSS typically does, lessening cognitive effort required for any reader who is already used to reading CSS.

Syntax

The new syntax is an extension of the existing syntax for interpolated strings. Previously, you could add a single $ before a string literal and use { and } to embed F# expressions within its contents. You can learn more about interpolated strings in the F# Language Reference.

Now, you can use multiple $ characters and corresponding numbers of opening and closing curly braces for interpolation, and the same rules also apply to % characters, which have special meaning in F# interpolated strings as format specifiers.

Here’s an example:

Code example showing new syntax on a JSON literal

Code
let order = 120.0M
let discount = 0.2M
let delivery = 20M
$$"""
{
    "order": {
        "value": {{order}},
        "currency": "USD"
    }
    "discount": "%%0.0f{{discount*100M}}%",
    "shipping": {
        "value": {{delivery}},
        "currency": "USD"
    },
    "total": {
        "value": {{(1M-discount) * order + delivery}},
        "currency": "USD"
    }
}
"""
// Produces:
// {
//     "order": {
//         "value": 120.0,
//         "currency": "USD"
//     }
//     "discount": "20%",
//     "shipping": {
//         "value": 20,
//         "currency": "USD"
//     },
//     "total": {
//         "value": 116.00,
//         "currency": "USD"
//     }
// }

In case you need to create a literal with longer sequences of { or }, the new syntax can still be used to avoid escaping – just start with more $s and use the same number of curly braces for interpolation.

This could come up when working with a templating engine, perhaps something like Angular or Vue.js templates in a hypothetical cross-compile scenario, and it might look something like this:

Code showing new syntax with 3 dollar signs

Code
let template = $$$"""
<div class="{{{classAttr}}}">
  <p>{{title}}</p>
  <div><img alt="item" src="{{itemImageUrl}}"></div>
  <button type="button" class="add" (click)="add(item)">Add</button>
</div>
"""

Note that now triple curly braces are needed for interpolation, but double or single braces are treated as regular content of the string.

Give it a go

At the moment to try this feature out, you have to use flag --langversion:preview which you can pass to dotnet fsi invocation or put it in your .fsproj file within <OtherFlags>.

4 comments

Discussion is closed. Login to edit/delete existing comments.

  • MÃ¥rten RÃ¥nge 1

    Hi.

    Looks good. Will this work as intended when you ask for an interpolated string to be converted into a FormattableString? When I last tried F# had some kind of bug in this area meaning escaping of {{ for the case of FormattableString didn’t work.

    Also how does it work behind the scenes? IIRC C# has some new fancy hi perf mode for string interpolation rather than relying on string.Format? How does it work in F#?

    • Adam BonieckiMicrosoft employee 1

      Hi MÃ¥rten,

      the bug you mentioned is already fixed, but the change is under –langversion:preview flag for now.

      Behind the scenes, this change was done pretty much exclusively at the lexing stage, so in general, implementation of string interpolation is the same as it was.

  • David N 1

    This is a great addition to the language. Next, I’d love to see more optimisation of string interpolation and support for the new way C# is going to do it without using formatting functions that have to parse a format string at runtime. Now it has seemingly no optimisations even for simple cases, so services like the ones I work on which generate a large amount of text are not as efficient as they could be.

    • Adam BonieckiMicrosoft employee 0

      Hi David,

      This sort of optimization is not currently being worked on or discussed as far as I know, but it would be a welcome addition.

      Out of curiosity – which of the “simple cases” do you think would improve performance of your services the most?

Feedback usabilla icon