{"id":58888,"date":"2025-11-17T10:05:00","date_gmt":"2025-11-17T18:05:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=58888"},"modified":"2025-11-17T10:05:00","modified_gmt":"2025-11-17T18:05:00","slug":"introducing-fsharp-10","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/introducing-fsharp-10\/","title":{"rendered":"Introducing F# 10"},"content":{"rendered":"<p>F# 10 is now shipping with .NET 10 and Visual Studio 2026.\nThis version is a refinement release focused on clarity, consistency, and performance, bringing small but meaningful improvements that make your everyday code more legible and robust.<\/p>\n<p>You can get F# 10 today in a few ways:<\/p>\n<ul>\n<li><a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/10.0\">Install the latest .NET 10 SDK<\/a><\/li>\n<li><a href=\"https:\/\/visualstudio.microsoft.com\/vs\/preview\/\">Install the latest Visual Studio 2026 preview<\/a><\/li>\n<\/ul>\n<p>With F# 10, we&#8217;re continuing our journey to make the language simpler and more performant.\nKey ergonomic improvements include <a href=\"#1-scoped-warning-suppression\">scoped warning suppression<\/a>,\nmore consistent <a href=\"#5-typed-bindings-in-computation-expressions-without-parentheses\">syntax for computation expressions<\/a>,\nand better support for <a href=\"#2-access-modifiers-on-auto-property-accessors\">auto property accessors<\/a>.\nThe release also includes an infrastructure upgrade to speed up compilation and interactive tooling in the form of the new <a href=\"#type-subsumption-cache\">type subsumption cache<\/a>.<\/p>\n<h2>Language Improvements<\/h2>\n<h3>1. Scoped warning suppression<\/h3>\n<p>The first feature I want to introduce for F# 10 is a much-requested one: the ability to suppress warnings in arbitrary code sections.\nOur compiler now supports the <code>#warnon<\/code> directive, which pairs with <code>#nowarn<\/code> to enable or disable warnings within a specific code span.<\/p>\n<p>Let&#8217;s take a look at a motivating example:<\/p>\n<pre><code class=\"language-fsharp\">\/\/ We know f is never called with a None.\nlet f (Some a) =    \/\/ creates warning 25, which we want to suppress\n    \/\/ 2000 loc, where the incomplete match warning is beneficial<\/code><\/pre>\n<p>Of course, we can add a <code>#nowarn 25<\/code> directive right above the function definition,\nbut without a matching <code>#warnon 25<\/code>, this will disable the FS0025 warning in the remainder of the file,\nrisking the suppression of legitimate issues elsewhere.<\/p>\n<p>With F# 10, you can delimit the section explicitly, applying the warning suppression to as narrow a scope as a single line:<\/p>\n<pre><code class=\"language-fsharp\">#nowarn 25\nlet f (Some x) =    \/\/ FS0025 suppressed\n#warnon 25\n    \/\/ FS0025 enabled again<\/code><\/pre>\n<p>Conversely, if a warning is disabled globally (e.g., via a compiler flag), you can enable it locally with <code>#warnon<\/code>.\nThis directive will then apply until a matching <code>#nowarn<\/code> or the end of the file.<\/p>\n<p><strong>Compatibility:<\/strong><\/p>\n<p>This feature is accompanied by several changes that improve the consistency of <code>#nowarn<\/code>\/<code>#warnon<\/code> directives, leading to more predictable behavior.\nHowever, these are breaking changes that might affect your codebase when you update to F# 10.\nA detailed list of potential issues can be found in the <a href=\"https:\/\/github.com\/fsharp\/fslang-design\/blob\/main\/RFCs\/FS-1146-scoped-nowarn.md\">RFC-1146<\/a>, but here are a couple of examples most likely to occur:<\/p>\n<ul>\n<li>Multiline and empty warn directives are no longer allowed.  <\/li>\n<li>Whitespace between <code>#<\/code> and <code>nowarn<\/code> is no longer allowed.  <\/li>\n<li>Triple-quoted, interpolated, or verbatim strings cannot be used for warning numbers.<\/li>\n<\/ul>\n<p>Script behavior is also affected.\nIn previous versions, a <code>#nowarn<\/code> directive anywhere in a script applied to the whole compilation.\nNow, its behavior in scripts matches that in <code>.fs<\/code> files, applying only until the end of the file or a corresponding <code>#warnon<\/code>.<\/p>\n<h3>2. Access modifiers on auto property accessors<\/h3>\n<p>A frequent pattern in object-oriented programming is to create publicly readable but privately mutable state.\nBefore F# 10, achieving this required explicit property syntax with backing fields, which added significant boilerplate:<\/p>\n<pre><code class=\"language-fsharp\">type Ledger() =\n    [&lt;DefaultValue&gt;] val mutable private _Balance: decimal\n    member this.Balance with public get() = this._Balance and private set v = this._Balance &lt;- v<\/code><\/pre>\n<p>With F# 10, you can apply distinct access modifiers to individual property accessors.\nThis allows you to specify different access levels for the getter and setter of a property inline,\nenabling common patterns like publicly readable but privately mutable state without verbose boilerplate.<\/p>\n<p>Now you can rewrite the above as:<\/p>\n<pre><code class=\"language-fsharp\">type Ledger() =\n    member val Balance = 0m with public get, private set<\/code><\/pre>\n<p><strong>Worth noting:<\/strong><\/p>\n<p>You can place an access modifier either before the property name (applying to both accessors) or before individual accessors, but not both simultaneously.<\/p>\n<p>Also, this feature does not extend to signature (.fsi) files.\nThe correct signature for the <code>Ledger<\/code> example above would be:<\/p>\n<pre><code class=\"language-fsharp\">type Ledger() =\n    member Balance : decimal\n    member private Balance : decimal with set<\/code><\/pre>\n<h3>3. <code>ValueOption<\/code> optional parameters<\/h3>\n<p>Optional parameters can now use a struct-based <code>ValueOption&lt;'T&gt;<\/code> representation.\nBy applying the <code>[&lt;Struct&gt;]<\/code> attribute to an optional parameter, you can instruct the compiler to use <code>ValueOption&lt;'T&gt;<\/code> instead of the reference-based <code>option<\/code> type.\nThis avoids a heap allocation for the option wrapper, which is beneficial in performance-critical code.<\/p>\n<p>Previous versions of F# always used the heap-allocated <code>option<\/code> type for optional parameters, even when the parameter was absent.\nFor high-throughput scenarios or inner-loop object creation, this imposed unnecessary GC pressure.\nDevelopers working on performance-sensitive code had no way to avoid these allocations:<\/p>\n<pre><code class=\"language-fsharp\">\/\/ Prior to F# 10: always uses reference option\ntype X() =\n    static member M(?x : string) =\n        match x with\n        | Some v -&gt; printfn \"Some %s\" v\n        | None -&gt; printfn \"None\"<\/code><\/pre>\n<p>Now you can use the <code>[&lt;Struct&gt;]<\/code> attribute on an optional parameter to leverage the struct-backed <code>ValueOption<\/code> and eliminate allocations when the argument is absent:<\/p>\n<pre><code class=\"language-fsharp\">type X() =\n    static member M([&lt;Struct&gt;] ?x : string) =\n        match x with\n        | ValueSome v -&gt; printfn \"ValueSome %s\" v\n        | ValueNone -&gt; printfn \"ValueNone\"<\/code><\/pre>\n<p>Choose this struct-based option for small values or frequently constructed types where allocation pressure matters.\nUse the default reference-based <code>option<\/code> when you rely on existing pattern matching helpers, need reference semantics, or when the performance difference is negligible.\nThis feature strengthens parity with other F# language constructs that already support <code>ValueOption<\/code>.<\/p>\n<h3>4. Tail-call support in computation expressions<\/h3>\n<p>Computation-expression builders (for example, coroutines or other builders implemented with resumable state machines) can now opt into tail-call optimizations.\nDuring desugaring, the compiler recognizes when an expression like <code>return!<\/code>, <code>yield!<\/code> or <code>do!<\/code> appears in a tail position and,\nwhen the builder provides special methods, routes those calls to the optimized entry points.<\/p>\n<p>If a builder implements:<\/p>\n<ul>\n<li><code>ReturnFromFinal<\/code>, the compiler will call it for a tail <code>return!<\/code> (falling back to <code>ReturnFrom<\/code> if the final variant is absent).<\/li>\n<li><code>YieldFromFinal<\/code>, the compiler will call it for a tail <code>yield!<\/code> (falling back to <code>YieldFrom<\/code> if absent).<\/li>\n<li>For a terminal <code>do!<\/code>, the compiler will prefer <code>ReturnFromFinal<\/code>, then <code>YieldFromFinal<\/code>, before falling back to the normal <code>Bind<\/code> pathway.<\/li>\n<\/ul>\n<p>These <code>*Final<\/code> members are optional and exist purely to enable optimization: they allow builders to short-circuit continuations or otherwise relinquish resources early.\nBuilders that do not provide these members keep their existing semantics unchanged.<\/p>\n<p>Examples:<\/p>\n<pre><code class=\"language-fsharp\">coroutine {\n    yield! subRoutine() \/\/ tail position -&gt; YieldFromFinal if available\n}<\/code><\/pre>\n<pre><code class=\"language-fsharp\">coroutine {\n    try\n        yield! subRoutine() \/\/ not tail -&gt; normal YieldFrom\n    finally ()\n}<\/code><\/pre>\n<p><strong>Compatibility:<\/strong><\/p>\n<p>This change can be breaking if a builder already defines members with these names.\nIn most cases, it is backward-compatible: existing builders continue to work without modification when compiled with F# 10.\nOlder compilers will simply ignore the new <code>*Final<\/code> methods, so builders that must remain compatible with earlier compiler versions should not assume those methods will be invoked.<\/p>\n<h3>5. Typed bindings in computation expressions without parentheses<\/h3>\n<p>A long-standing inconsistency in type annotation syntax for computation expression bindings has been resolved.\nYou can now add type annotations on <code>let!<\/code>, <code>use!<\/code>, and <code>and!<\/code> bindings without wrapping the identifier in parentheses.<\/p>\n<p>Prior versions of F# required parentheses for type annotations in computation expression bindings.\nFor example, <code>let! (x: int) = fetchA()<\/code> was valid, but the more natural <code>let! x: int = fetchA()<\/code> would cause an error.\nThis forced developers to use visually noisy parentheses even for simple type annotations:<\/p>\n<pre><code class=\"language-fsharp\">async {\n    let! (a: int) = fetchA()\n    and! (b: int) = fetchB()\n    use! (d: MyDisposable) = acquireAsync()\n    return a + b\n}<\/code><\/pre>\n<p>Now you can write type annotations without parentheses, matching the style of ordinary <code>let<\/code> bindings:<\/p>\n<pre><code class=\"language-fsharp\">async {\n    let! a: int = fetchA()\n    and! b: int = fetchB()\n    use! d: MyDisposable = acquireAsync()\n    return a + b\n}<\/code><\/pre>\n<h3>6. Allow <code>_<\/code> in <code>use!<\/code> bindings<\/h3>\n<p>Prior versions of F# rejected the discard pattern in <code>use!<\/code> bindings, even when the resource value itself was never referenced.\nThis inconsistency with regular <code>use<\/code> bindings forced developers to create throwaway identifiers like <code>__<\/code> or <code>_ignored<\/code> just to satisfy the compiler.<\/p>\n<p>The discard pattern (<code>_<\/code>) now works in <code>use!<\/code> bindings within computation expressions.\nF# 10 allows you to use <code>_<\/code> directly when binding asynchronous resources whose values\nare only needed for lifetime management, without being forced to provide a named identifier.<\/p>\n<p>Now you can use the discard pattern directly, clarifying your intent and matching the behavior of <code>use<\/code>:<\/p>\n<pre><code class=\"language-fsharp\">counterDisposable {\n    use! _ = new Disposable()\n    \/\/ logic\n}<\/code><\/pre>\n<h3>7. Rejecting pseudo-nested modules in types<\/h3>\n<p>Structural validation has been tightened in this release to reject misleading module placement within types.\nF# 10 now raises an error when a <code>module<\/code> declaration appears indented at the same structural level\ninside a type definition, preventing a common source of confusion about module scoping.<\/p>\n<p>Previous versions of F# accepted <code>module<\/code> declarations indented within type definitions,\nbut these modules were actually created as siblings to the type rather than being nested within it.\nThis indentation pattern frequently misled developers into believing they had created a nested module, resulting in unexpected scoping behavior:<\/p>\n<pre><code class=\"language-fsharp\">type U =\n    | A\n    | B\n    module M = \/\/ Silently created a sibling module, not nested\n        let f () = ()<\/code><\/pre>\n<p>Now, this pattern raises error FS0058, forcing you to clarify your intent with proper module placement:<\/p>\n<pre><code class=\"language-fsharp\">type U =\n    | A\n    | B\n\nmodule M =\n    let f () = ()<\/code><\/pre>\n<h3>8. Deprecation warning for omitted <code>seq<\/code><\/h3>\n<p>A deprecation warning now appears for bare sequence expressions that omit the <code>seq<\/code> builder.\nF# 10 warns when you use bare range braces like <code>{ 1..10 }<\/code> or similar forms, encouraging you to use the\nexplicit <code>seq { ... }<\/code> form for consistency with the broader computation expression model.<\/p>\n<p>Historically, F# allowed a special-case \u201csequence comprehension lite\u201d syntax where the <code>seq<\/code> keyword could be omitted.\nThis diverged from how other computation expressions work and created an inconsistency in the language:<\/p>\n<pre><code class=\"language-fsharp\">{ 1..10 } |&gt; List.ofSeq  \/\/ implicit sequence<\/code><\/pre>\n<p>Now, the compiler warns about this pattern and encourages the explicit form that clarifies semantics:<\/p>\n<pre><code class=\"language-fsharp\">seq { 1..10 } |&gt; List.ofSeq<\/code><\/pre>\n<p>This is currently a warning, not an error, giving you time to update your codebase.\nThe explicit <code>seq<\/code> form improves code clarity and consistency with other computation expressions.\nFuture versions of F# may make this an error, so we recommend adopting the explicit syntax when updating code.<\/p>\n<h3>9. Attribute target enforcement<\/h3>\n<p>Attribute target validation will be enforced with this release across all language constructs.\nF# 10 validates that attributes are only applied to their intended targets by checking <code>AttributeTargets<\/code>\nacross let-bound values, functions, union cases, implicit constructors, structs, and classes.<\/p>\n<p>Previous versions of F# silently allowed attributes to be misapplied to incompatible targets.\nThis caused subtle bugs, such as test attributes being ignored when you forgot <code>()<\/code> to make a function\nor analyzer directives failing to take effect, leading to confusing CI discrepancies:<\/p>\n<pre><code class=\"language-fsharp\">[&lt;Fact&gt;]\nlet ``this is not a function`` =  \/\/ Silently ignored, not a test!\n    Assert.True(false)<\/code><\/pre>\n<p>Now, the compiler enforces attribute targets and raises a warning when attributes are misapplied:<\/p>\n<pre><code class=\"language-fsharp\">[&lt;Fact&gt;]\n\/\/^^^^ - warning FS0842: This attribute cannot be applied to property, field, return value. Valid targets are: method\nlet ``works correctly`` =\n    Assert.True(true)<\/code><\/pre>\n<p><strong>Compatibility:<\/strong><\/p>\n<p>This is a breaking change that may reveal previously silent issues in your codebase.\nThe early errors prevent test discovery problems and ensure that attributes like analyzers and decorators take effect as intended.<\/p>\n<h2>FSharp.Core enhancement &#8211; support for <code>and!<\/code> in task expressions<\/h2>\n<p>This release brings a single improvement to the FSharp.Core library: support for <code>and!<\/code> in the <code>task<\/code> computation expression.<\/p>\n<p>Using <code>task<\/code> is a popular way to work with asynchronous workflows in F#, especially when interop with C# is required.\nHowever, until recently there was no concise way to await multiple tasks concurrently in a computation expression.<\/p>\n<p>Perhaps you started with code that awaited computations sequentially:<\/p>\n<pre><code class=\"language-fsharp\">\/\/ Awaiting sequentially\ntask {\n    let! a = fetchA()\n    let! b = fetchB()\n    return combineAB a b\n}<\/code><\/pre>\n<p>If you then wanted to change it to await them concurrently, you would typically use <code>Task.WhenAll<\/code>:<\/p>\n<pre><code class=\"language-fsharp\">\/\/ Use explicit Task combinator to await concurrently\ntask {\n    let ta = fetchA()\n    let tb = fetchB()\n    let! results = Task.WhenAll([| ta; tb |])\n    return combineAB ta.Result tb.Result\n}<\/code><\/pre>\n<p>With F# 10, you can instead write a more idiomatic version using <code>and!<\/code>:<\/p>\n<pre><code class=\"language-fsharp\">task {\n    let! a = fetchA()\n    and! b = fetchB()\n    return combineAB a b\n}<\/code><\/pre>\n<p>This combines the semantics of the second snippet with the simplicity of the first.<\/p>\n<h2>Performance &amp; Tooling<\/h2>\n<h3>Type subsumption cache<\/h3>\n<p>This release also introduces a new type subsumption cache to accelerate type checking and improve IDE responsiveness, especially in projects with complex type hierarchies.\nIn F# 10, the compiler memoizes the results of type relationship checks, reducing redundant computations and improving overall compiler and tooling performance.<\/p>\n<p>Previously, the F# compiler would repeatedly perform expensive subsumption checks when dealing with large type hierarchies, such as those involving numerous numeric primitives or many interface implementations.\nThis could lead to noticeable latency during type inference and IDE operations, particularly in large solutions or during long editing sessions, consuming extra CPU and memory.<\/p>\n<p>Now, the new type subsumption cache stores the results of these checks.\nWhen the compiler needs to determine if one type can be used in place of another, it first consults the cache.\nThis memoization avoids re-computing the same type relationships, leading to faster compilation and more responsive IntelliSense.<\/p>\n<p>No code changes are required to benefit from this improvement; the performance gains are automatic.<\/p>\n<h3>Better trimming by default<\/h3>\n<p>F# 10 removes a long\u2011standing bit of friction with trimming F# assemblies:\nyou no longer have to hand\u2011maintain an <code>ILLink.Substitutions.xml<\/code> file just to strip large F# metadata resource blobs (signature\/optimization data) that aren&#8217;t needed at runtime in the final application.<\/p>\n<p>When you publish with trimming enabled (<code>PublishTrimmed=true<\/code>), the F# build now auto\u2011generates a substitutions file that targets the tooling\u2011only F# resources.\nThe result: smaller output by default, less boilerplate, and one fewer maintenance hazard.<\/p>\n<p>If you need full manual control you can still add your own substitutions file.\nThe auto-generation can be turned off with a property, like so: <code>&lt;DisableILLinkSubstitutions&gt;false&lt;\/DisableILLinkSubstitutions&gt;<\/code>.<\/p>\n<h3>Parallel compilation in preview<\/h3>\n<p>An exciting update for F# users looking to reduce their projects&#8217; compilation times is the progressing stabilization of the parallel compilation features.\nStarting with .NET 10, three features: graph-based type checking, parallel IL code generation, and parallel optimization are grouped together under the <code>ParallelCompilation<\/code> project property.<\/p>\n<p>Currently, this setting is turned on by default for projects that are using <code>LangVersion=Preview<\/code>, and we plan to turn it on for everyone in .NET 11.\nBe sure to give it a try and see if it speeds up your compilation.\nHowever, if you want to opt out but still enjoy other preview features, then set the <code>ParallelCompilation<\/code> to false.<\/p>\n<h3>Typecheck-only mode for scripts<\/h3>\n<p>F# is a great scripting language, and we want to ensure that the tooling also supports that use case.\nThat is why we&#8217;re extending the <code>--typecheck-only<\/code> compilation flag to also work for <code>.fsx<\/code> scripts, where it&#8217;s arguably the most beneficial.\nIn F# 9 and earlier, there was no easy way to validate syntax and type correctness of a script without running the code, which would often produce side effects.<\/p>\n<p>With this release, you can simply use the <code>--typecheck-only<\/code> flag while invoking <code>fsi<\/code> to typecheck the script without execution.\nThis feature makes it easy to add CI gates that will be able to catch script rot in your codebase.<\/p>\n<p><strong>Known issue caveat:<\/strong>\nAs of publishing this post, there is a <a href=\"https:\/\/github.com\/dotnet\/fsharp\/issues\/19047\">known bug<\/a> that severely limits the benefits of using <code>--typecheck-only<\/code> with scripts that include others via <code>#load<\/code>.\nIn that case type checking will conclude prematurely after processing the first loaded source file. However, the fix has already been implemented and will be included in the upcoming patch release 10.0.200.<\/p>\n<h2>Thanks and acknowledgements<\/h2>\n<p>F# is developed as a collaboration between the .NET Foundation, the F# Software Foundation, their members and other contributors including Microsoft.\nThe F# community is involved at all stages of innovation, design, implementation and delivery and we\u2019re proud to be a contributing part of this community.<\/p>\n<p>In the past year, we merged commits from 25 contributors.\nWe would like to thank all of you, as well as many others who filed <a href=\"https:\/\/github.com\/dotnet\/fsharp\/issues\">issues<\/a>, raised and voted on <a href=\"https:\/\/github.com\/fsharp\/fslang-suggestions\">language suggestions<\/a>, and contributed to <a href=\"https:\/\/github.com\/fsharp\/fslang-design\">language design<\/a>.\nIn particular, we explicitly call out some of the most active community contributors:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/majocha\">@majocha<\/a> for a multitude of contributions, including infrastructure, testing and tooling improvements.<\/li>\n<li><a href=\"https:\/\/github.com\/Martin521\">@Martin521<\/a> most notably for a herculean effort on the scoped <code>#nowarn<\/code> feature design and implementation.<\/li>\n<li><a href=\"https:\/\/github.com\/edgarfgp\">@edgarfgp<\/a> for his continued efforts in the area of error handling and diagnostics, as well as great improvements to the parser.<\/li>\n<li><a href=\"https:\/\/github.com\/auduchinok\">@auduchinok<\/a> for his work on improving tooling via fixes to parser recovery and other areas, as well as contributions to testing infrastructure.<\/li>\n<\/ul>\n<h2>Contributor showcase<\/h2>\n<p>As is tradition in our release announcements, the contributor showcase section is where we celebrate some of our most impactful open-source community contributors.\nWhile our long-time contributor Jakub Majocha received a well-deserved <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-dotnet-8-rc1\/#community-contributor\">shout-out in a .NET 8 announcement<\/a> two years ago, his outstanding work over the past year on the F# compiler has earned him one here in the F# release blog post.\nThis time, he is going to share a little about his approach to contributing to the repo:<\/p>\n<blockquote>\n<p>Looking back at my PR history I see that most of them are fixes to things that I broke \ud83d\ude42\nThat&#8217;s why as a hobbyist I try to stick to contributions that have low risk of breaking something serious. For example I had a lot of fun making the tests run in parallel.\nI&#8217;m also always looking for low-hanging fruit, there still are quite a few. For example you can revive some unfinished PRs that were already almost done:\nType subsumption cache was one and the other was tail-call support in computation expressions &#8211; my first ever RFC implemented.\nI have an interest in tooling and its performance, so another surprising win that I&#8217;m very happy with is reducing StackGuard related slowdowns.\nI had a hunch about StackGuard impact on editor performance, and experimented for some time with various approaches.\nAdding some instrumentation to collect metrics was the most helpful here and the solution turned out to be very straightforward.\nI&#8217;d like to add huge thanks to the Amplifying F# collective for encouraging my attempts and sponsoring my Copilot subscription.<\/p>\n<\/blockquote>\n<p>If you read it carefully, you might have noticed a teaser of an upcoming performance improvement that didn&#8217;t make it to this release, but we are very excited to bring to F# 11.<\/p>\n<p>And just like Jakub, we would like to recognize <a href=\"https:\/\/amplifyingfsharp.io\/\">Amplifying F#<\/a> for their invaluable support of community contributors.\nEfforts like theirs help make the F# ecosystem thrive.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>Work on F# 11 is already under way: more performance wins, language\nimprovements, and tooling upgrades.<\/p>\n<p>Go to our GitHub repo to jump into <a href=\"https:\/\/github.com\/dotnet\/fsharp\/discussions\">discussions<\/a>, <a href=\"https:\/\/github.com\/dotnet\/fsharp\/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22\">help wanted issues<\/a> or <a href=\"https:\/\/github.com\/dotnet\/fsharp\/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22\">good first issues for new contributors<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Learn about new F# 10 language features, performance upgrades, and tooling improvements shipping with .NET 10.<\/p>\n","protected":false},"author":123807,"featured_media":58889,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,636],"tags":[7892,63,73,8085],"class_list":["post-58888","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-fsharp","tag-dotnet-10","tag-dotnet","tag-f","tag-f-10"],"acf":[],"blog_post_summary":"<p>Learn about new F# 10 language features, performance upgrades, and tooling improvements shipping with .NET 10.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/58888","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=58888"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/58888\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58889"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=58888"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=58888"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=58888"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}