{"id":46641,"date":"2023-07-27T10:05:00","date_gmt":"2023-07-27T17:05:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=46641"},"modified":"2023-07-27T09:47:32","modified_gmt":"2023-07-27T16:47:32","slug":"new-syntax-for-string-interpolation-in-fsharp","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/new-syntax-for-string-interpolation-in-fsharp\/","title":{"rendered":"New syntax for string interpolation in F#"},"content":{"rendered":"<p>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.\nThis feature is modeled after how interpolation works in C#&#8217;s <a href=\"https:\/\/learn.microsoft.com\/dotnet\/csharp\/programming-guide\/strings\/#raw-string-literals\">raw strings<\/a>, but maintains backwards compatibility with F#&#8217;s triple quoted strings.<\/p>\n<p>Interpolated strings are a very convenient way for developers to embed F# expressions into string literals.\nHowever, one scenario where working with interpolated strings can become cumbersome is dealing with text that contains many curly braces.\nThat&#8217;s where the new F# interpolation syntax comes in. <\/p>\n<h2>Overview<\/h2>\n<p>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 <a href=\"https:\/\/fable.io\/\">Fable<\/a>.\nWith the new syntax, you can write your CSS without worrying about escaping curly braces, allowing you to focus on the interpolation expressions themselves.\nHere&#8217;s an example:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2023\/07\/overview_example.png\" alt=\"Code example showing new interpolated string syntax on a CSS content\" \/><\/p>\n<details>\n<summary>Code<\/summary>\n<pre><code class=\"language-fsharp\">let transitionMs = 50\r\nlet css = $$\"\"\"\r\n:host {\r\n    transition-duration: {{transitionMs}}ms;\r\n    border: 2px solid lightgray;\r\n    border-radius: 10px;\r\n    margin: 5px 0;\r\n}\r\n\r\n:host.transition-enter {\r\n    opacity: 0;\r\n    transform: scale(2);\r\n}\r\n:host.transition-leave {\r\n    opacity: 0;\r\n    transform: scale(0.1);\r\n}\r\n.is-clickable {\r\n    user-select: none;\r\n}\r\n\"\"\"<\/code><\/pre>\n<\/details>\n<p>You don&#8217;t have to comb through all your string literals looking for <code>{<\/code>, <code>}<\/code> or <code>%<\/code> characters to escape, you can just write (or copy-paste) them as you normally would.\nAnd the end result looks even more like CSS typically does, lessening cognitive effort required for any reader who is already used to reading CSS.<\/p>\n<h2>Syntax<\/h2>\n<p>The new syntax is an extension of the existing syntax for interpolated strings.\nPreviously, you could add a single <code>$<\/code> before a string literal and use <code>{<\/code> and <code>}<\/code> to embed F# expressions within its contents.\nYou can learn more about interpolated strings in the <a href=\"https:\/\/learn.microsoft.com\/dotnet\/fsharp\/language-reference\/interpolated-strings\">F# Language Reference<\/a>.<\/p>\n<p>Now, you can use multiple <code>$<\/code> characters and corresponding numbers of opening and closing curly braces for interpolation, and the same rules also apply to <code>%<\/code> characters, which have special meaning in F# interpolated strings as format specifiers.<\/p>\n<p>Here&#8217;s an example:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2023\/07\/syntax_example1.png\" alt=\"Code example showing new syntax on a JSON literal\" \/><\/p>\n<details>\n<summary>Code<\/summary>\n<pre><code class=\"language-fsharp\">let order = 120.0M\r\nlet discount = 0.2M\r\nlet delivery = 20M\r\n$$\"\"\"\r\n{\r\n    \"order\": {\r\n        \"value\": {{order}},\r\n        \"currency\": \"USD\"\r\n    }\r\n    \"discount\": \"%%0.0f{{discount*100M}}%\",\r\n    \"shipping\": {\r\n        \"value\": {{delivery}},\r\n        \"currency\": \"USD\"\r\n    },\r\n    \"total\": {\r\n        \"value\": {{(1M-discount) * order + delivery}},\r\n        \"currency\": \"USD\"\r\n    }\r\n}\r\n\"\"\"\r\n\/\/ Produces:\r\n\/\/ {\r\n\/\/     \"order\": {\r\n\/\/         \"value\": 120.0,\r\n\/\/         \"currency\": \"USD\"\r\n\/\/     }\r\n\/\/     \"discount\": \"20%\",\r\n\/\/     \"shipping\": {\r\n\/\/         \"value\": 20,\r\n\/\/         \"currency\": \"USD\"\r\n\/\/     },\r\n\/\/     \"total\": {\r\n\/\/         \"value\": 116.00,\r\n\/\/         \"currency\": \"USD\"\r\n\/\/     }\r\n\/\/ }<\/code><\/pre>\n<\/details>\n<p>In case you need to create a literal with longer sequences of <code>{<\/code> or <code>}<\/code>, the new syntax can still be used to avoid escaping &#8211; just start with more <code>$<\/code>s and use the same number of curly braces for interpolation.<\/p>\n<p>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:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2023\/07\/syntax_example2_2.png\" alt=\"Code showing new syntax with 3 dollar signs\" \/><\/p>\n<details>\n<summary>Code<\/summary>\n<pre><code class=\"language-html\">let template = $$$\"\"\"\r\n&lt;div class=&quot;{{{classAttr}}}&quot;&gt;\r\n  &lt;p&gt;{{title}}&lt;\/p&gt;\r\n  &lt;div&gt;&lt;img alt=&quot;item&quot; src=&quot;{{itemImageUrl}}&quot;&gt;&lt;\/div&gt;\r\n  &lt;button type=&quot;button&quot; class=&quot;add&quot; (click)=&quot;add(item)&quot;&gt;Add&lt;\/button&gt;\r\n&lt;\/div&gt;\r\n\"\"\"<\/code><\/pre>\n<\/details>\n<p>Note that now triple curly braces are needed for interpolation, but double or single braces are treated as regular content of the string.<\/p>\n<h2>Give it a go<\/h2>\n<p>At the moment to try this feature out, you have to use flag <code>--langversion:preview<\/code> which you can pass to <code>dotnet fsi<\/code> invocation or put it in your <code>.fsproj<\/code> file within <code>&lt;OtherFlags&gt;<\/code>.<\/p>\n<h2>Links to official documentation<\/h2>\n<ul>\n<li><a href=\"https:\/\/learn.microsoft.com\/dotnet\/fsharp\/language-reference\/interpolated-strings#extended-syntax-for-string-interpolation\">F# Language Reference<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/fsharp\/fslang-design\/blob\/main\/RFCs\/FS-1132-better-interpolated-triple-quoted-strings.md\">RFC FS-1132 &#8211; Extended syntax for interpolated strings<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>New language feature is available in preview for F# to make working with interpolated strings even easier.<\/p>\n","protected":false},"author":123807,"featured_media":46655,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,636],"tags":[73],"class_list":["post-46641","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-fsharp","tag-f"],"acf":[],"blog_post_summary":"<p>New language feature is available in preview for F# to make working with interpolated strings even easier.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/46641","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/123807"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=46641"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/46641\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/46655"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=46641"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=46641"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=46641"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}