From fcaecde22ebd0c4a0cee3069055b7aaf75f1cf2d Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:01:03 +0000 Subject: [PATCH 1/7] What's new in F# 10 --- docs/fsharp/whats-new/fsharp-10.md | 381 +++++++++++++++++++++++++++++ 1 file changed, 381 insertions(+) create mode 100644 docs/fsharp/whats-new/fsharp-10.md diff --git a/docs/fsharp/whats-new/fsharp-10.md b/docs/fsharp/whats-new/fsharp-10.md new file mode 100644 index 0000000000000..6eb97bbba40f0 --- /dev/null +++ b/docs/fsharp/whats-new/fsharp-10.md @@ -0,0 +1,381 @@ +--- +title: What's new in F# 10 - F# Guide +description: Get an overview of the new features available in F# 10. +ms.date: 11/11/2025 +ms.topic: whats-new +--- +# What's new in F# 10 + +F# 10 brings you several improvements to the F# language, FSharp.Core library, and tooling. +This version is a refinement release focused on clarity, consistency, and performance, with small but meaningful improvements that make your everyday code more legible and robust. +F# 10 ships with **.NET 10** and **Visual Studio 2022**. + +You can download the latest .NET SDK from the [.NET downloads page](https://dotnet.microsoft.com/download). + +## Get started + +F# 10 is available in all .NET Core distributions and Visual Studio tooling. +For more information, see [Get started with F#](../get-started/index.md). + +## Scoped warning suppression + +You can now suppress warnings in specific sections of your code using the new `#warnon` directive. +This pairs with the existing `#nowarn` directive to give you precise control over which warnings apply where. + +Previously, when you used `#nowarn`, it would disable a warning for the remainder of the file, which could suppress legitimate issues elsewhere. +Let's look at a motivating example: + +```fsharp +// We know f is never called with a None. +let f (Some a) = // creates warning 25, which we want to suppress + // 2000 loc, where the incomplete match warning is beneficial +``` + +If you add `#nowarn 25` above the function definition, it disables FS0025 for the entire remainder of the file. + +With F# 10, you can now mark the exact section where you want the warning suppressed: + +```fsharp +#nowarn 25 +let f (Some x) = // FS0025 suppressed +#warnon 25 + // FS0025 enabled again +``` + +Conversely, if a warning is disabled globally (for example, via a compiler flag), you can enable it locally with `#warnon`. +This directive will apply until a matching `#nowarn` or the end of the file. + +**Important compatibility notes:** + +This feature includes several changes that improve the consistency of `#nowarn`/`#warnon` directives. +These are breaking changes: + +* The compiler no longer allows multiline and empty warn directives. +* The compiler no longer allows whitespace between `#` and `nowarn`. +* You cannot use triple-quoted, interpolated, or verbatim strings for warning numbers. + +Script behavior has also changed. +Previously, when you added a `#nowarn` directive anywhere in a script, it applied to the whole compilation. +Now, its behavior in scripts matches that in `.fs` files, applying only until the end of the file or a corresponding `#warnon`. + +This feature implements [RFC FS-1146](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1146-scoped-nowarn.md). + +## Access modifiers on auto property accessors + +A common pattern in object-oriented programming is to create publicly readable but privately mutable state. +Before F# 10, you needed explicit property syntax with backing fields (hidden variables that store the actual property values) to achieve this, which added repetitive code: + +```fsharp +type Ledger() = + [] val mutable private _Balance: decimal + member this.Balance with public get() = this._Balance and private set v = this._Balance <- v +``` + +With F# 10, you can now apply different access modifiers to individual property accessors. +This lets you specify different access levels for the getter and setter of a property, making the pattern much simpler: + +```fsharp +type Ledger() = + member val Balance = 0m with public get, private set +``` + +You can place an access modifier either before the property name (applying to both accessors) or before individual accessors, but not both simultaneously. + +Note that this feature does not extend to signature (`.fsi`) files. +The correct signature for the `Ledger` example above is: + +```fsharp +type Ledger() = + member Balance : decimal + member private Balance : decimal with set +``` + +This feature implements [RFC FS-1141](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1141-Allow-access-modifiers-to-auto-properties-getters-and-setters.md). + +## ValueOption optional parameters + +You can now use a struct-based `ValueOption<'T>` representation for optional parameters. +When you apply the `[]` attribute to an optional parameter, the compiler uses `ValueOption<'T>` instead of the reference-based `option` type. +This avoids heap allocations (memory allocated on the managed heap that requires garbage collection) for the option wrapper, which is beneficial in performance-critical code. + +Previously, F# always used the heap-allocated `option` type for optional parameters, even when the parameter was absent: + +```fsharp +// Prior to F# 10: always uses reference option +type X() = + static member M(?x : string) = + match x with + | Some v -> printfn "Some %s" v + | None -> printfn "None" +``` + +In F# 10, you can use the `[]` attribute to leverage the struct-backed `ValueOption`: + +```fsharp +type X() = + static member M([] ?x : string) = + match x with + | ValueSome v -> printfn "ValueSome %s" v + | ValueNone -> printfn "ValueNone" +``` + +This eliminates heap allocations when the argument is absent, which is beneficial in performance-critical code. + +Choose this struct-based option for small values or frequently constructed types where allocation pressure matters. +Use the default reference-based `option` when you rely on existing pattern matching helpers, need reference semantics, or when the performance difference is negligible. +This feature strengthens parity with other F# language constructs that already support `ValueOption`. + +## Tail-call support in computation expressions + +F# 10 adds tail-call optimizations for computation expressions. +Computation-expression builders can now opt into these optimizations by implementing special methods. + +When the compiler translates computation expressions into regular F# code (a process called desugaring), it recognizes when an expression like `return!`, `yield!`, or `do!` appears in a tail position. +If your builder provides the following methods, the compiler routes those calls to optimized entry points: + +* `ReturnFromFinal` - called for a tail `return!` (falls back to `ReturnFrom` if absent) +* `YieldFromFinal` - called for a tail `yield!` (falls back to `YieldFrom` if absent) +* For a terminal `do!`, the compiler prefers `ReturnFromFinal`, then `YieldFromFinal`, before falling back to the normal `Bind` pathway + +These `*Final` members are optional and exist purely to enable optimization. +Builders that do not provide these members keep their existing semantics unchanged. + +For example: + +```fsharp +coroutine { + yield! subRoutine() // tail position -> YieldFromFinal if available +} +``` + +However, in a non-tail position: + +```fsharp +coroutine { + try + yield! subRoutine() // not tail -> normal YieldFrom + finally () +} +``` + +**Important compatibility note:** + +This change can be breaking if a computation expression builder already defines members with these names. +In most cases, existing builders continue to work without modification when compiled with F# 10. +Older compilers will ignore the new `*Final` methods, so builders that must remain compatible with earlier compiler versions should not assume the compiler will invoke these methods. + +This feature implements [RFC FS-1330](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1330-support-tailcalls-in-computation-expressions.md). + +## Typed bindings in computation expressions without parentheses + +F# 10 removes the requirement for parentheses when adding type annotations to computation expression bindings. +You can now add type annotations on `let!`, `use!`, and `and!` bindings using the same syntax as ordinary `let` bindings. + +Previously, you had to use parentheses for type annotations: + +```fsharp +async { + let! (a: int) = fetchA() + and! (b: int) = fetchB() + use! (d: MyDisposable) = acquireAsync() + return a + b +} +``` + +In F# 10, you can write type annotations without parentheses: + +```fsharp +async { + let! a: int = fetchA() + and! b: int = fetchB() + use! d: MyDisposable = acquireAsync() + return a + b +} +``` + +## Allow `_` in `use!` bindings + +You can now use the discard pattern (`_`) in `use!` bindings within computation expressions. +This aligns the behavior of `use!` with regular `use` bindings. + +Previously, the compiler rejected the discard pattern in `use!` bindings, forcing you to create throwaway identifiers: + +```fsharp +counterDisposable { + use! _ignored = new Disposable() + // logic +} +``` + +In F# 10, you can use the discard pattern directly: + +```fsharp +counterDisposable { + use! _ = new Disposable() + // logic +} +``` + +This clarifies intent when binding asynchronous resources whose values are only needed for lifetime management. + +## Rejecting pseudo-nested modules in types + +The compiler now raises an error when you place a `module` declaration indented at the same structural level inside a type definition. +This tightens structural validation to reject misleading module placement within types. + +Previously, the compiler accepted `module` declarations indented within type definitions, but it actually created these modules as siblings to the type rather than nesting them within it: + +```fsharp +type U = + | A + | B + module M = // Silently created a sibling module, not nested + let f () = () +``` + +With F# 10, this pattern raises error FS0058, forcing you to clarify your intent with proper module placement: + +```fsharp +type U = + | A + | B + +module M = + let f () = () +``` + +## Deprecation warning for omitted `seq` + +The compiler now warns you about bare sequence expressions that omit the `seq` builder. +When you use bare range braces like `{ 1..10 }`, you'll see a deprecation warning encouraging you to use the explicit `seq { ... }` form. + +Historically, F# allowed a special-case "sequence comprehension lite" syntax where you could omit the `seq` keyword: + +```fsharp +{ 1..10 } |> List.ofSeq // implicit sequence +``` + +In F# 10, the compiler warns about this pattern and encourages the explicit form: + +```fsharp +seq { 1..10 } |> List.ofSeq +``` + +This is currently a warning, not an error, giving you time to update your codebase. +The explicit `seq` form improves code clarity and consistency with other computation expressions. +Future versions of F# may make this an error, so we recommend adopting the explicit syntax when you update your code. + +This feature implements [RFC FS-1033](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1033-Deprecate-places-where-seq-can-be-omitted.md). + +## Attribute target enforcement + +F# 10 enforces attribute target validation across all language constructs. +The compiler now validates that attributes are only applied to their intended targets by checking `AttributeTargets` across let-bound values, functions, union cases, implicit constructors, structs, and classes. + +Previously, the compiler silently allowed you to misapply attributes to incompatible targets. +This caused subtle bugs, such as test attributes being ignored when you forgot `()` to make a function: + +```fsharp +[] +let ``this is not a function`` = // Silently ignored, not a test! + Assert.True(false) +``` + +In F# 10, the compiler enforces attribute targets and raises an error when attributes are misapplied: + +```fsharp +[] +let ``works correctly`` () = // Correct: function with unit parameter + Assert.True(true) +``` + +**Important compatibility note:** + +This is a breaking change that may reveal previously silent issues in your codebase. +The early errors prevent test discovery problems and ensure that attributes like analyzers and decorators take effect as intended. + +## Support for `and!` in task expressions + +You can now await multiple tasks concurrently using `and!` in task expressions. +Using `task` is a popular way to work with asynchronous workflows in F#, especially when you need interoperability with C#. +However, until now there was no concise way to await multiple tasks concurrently in a computation expression. + +Perhaps you started with code that awaited computations sequentially: + +```fsharp +// Awaiting sequentially +task { + let! a = fetchA() + let! b = fetchB() + return combineAB a b +} +``` + +If you then wanted to change it to await them concurrently, you would typically use `Task.WhenAll`: + +```fsharp +// Use explicit Task combinator to await concurrently +task { + let ta = fetchA() + let tb = fetchB() + let! results = Task.WhenAll([| ta; tb |]) + return combineAB ta.Result tb.Result +} +``` + +In F# 10, you can use `and!` for a more idiomatic approach: + +```fsharp +task { + let! a = fetchA() + and! b = fetchB() + return combineAB a b +} +``` + +This combines the semantics of the concurrent version with the simplicity of the sequential version. + +This feature implements F# language suggestion [#1363](https://github.com/fsharp/fslang-suggestions/issues/1363). + +## Better trimming by default + +F# 10 removes a long-standing bit of friction with trimming F# assemblies. +Trimming is the process of removing unused code from your published application to reduce its size. +You no longer have to manually maintain an `ILLink.Substitutions.xml` file just to strip large F# metadata resource blobs (signature and optimization data that the compiler uses but your application doesn't need at runtime). + +When you publish with trimming enabled (`PublishTrimmed=true`), the F# build now automatically generates a substitutions file that targets these tooling-only F# resources. + +Previously, you had to manually maintain this file to strip the metadata. +This added maintenance burden and was easy to forget. + +The result is smaller output by default, less repetitive code to maintain, and one fewer maintenance hazard. +If you need full manual control, you can still add your own substitutions file. +You can turn off the auto-generation with the `false` property. + +## Parallel compilation in preview + +An exciting update for F# users looking to reduce compilation times: the parallel compilation features are stabilizing. +Starting with .NET 10, three features—graph-based type checking, parallel IL code generation, and parallel optimization—are grouped together under the `ParallelCompilation` project property. + +F# 10 enables this setting by default for projects using `LangVersion=Preview`. +We plan to enable it for all projects in .NET 11. + +Be sure to give it a try and see if it speeds up your compilation. +To enable parallel compilation in F# 10: + +```xml + + true + +``` + +If you want to opt out while still enjoying other preview features, set `ParallelCompilation` to false: + +```xml + + Preview + false + +``` + +Parallel compilation can significantly reduce compilation times for projects with multiple files and dependencies. \ No newline at end of file From 59f79930d9182806683f68a9c5d27e3456ae1a2b Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:15:47 +0000 Subject: [PATCH 2/7] Add ai-usage metadata --- docs/fsharp/whats-new/fsharp-10.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/fsharp/whats-new/fsharp-10.md b/docs/fsharp/whats-new/fsharp-10.md index 6eb97bbba40f0..2b0f95f4247e2 100644 --- a/docs/fsharp/whats-new/fsharp-10.md +++ b/docs/fsharp/whats-new/fsharp-10.md @@ -3,6 +3,7 @@ title: What's new in F# 10 - F# Guide description: Get an overview of the new features available in F# 10. ms.date: 11/11/2025 ms.topic: whats-new +ai-usage: ai-assisted --- # What's new in F# 10 From 95c7d425e4bbc17a6080abab4d244311fd36524c Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Fri, 7 Nov 2025 09:51:00 +0000 Subject: [PATCH 3/7] Add link in .net 10 overview to F# --- docs/core/whats-new/dotnet-10/overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/core/whats-new/dotnet-10/overview.md b/docs/core/whats-new/dotnet-10/overview.md index 1ffca77d92077..bbb8b42c6e51e 100644 --- a/docs/core/whats-new/dotnet-10/overview.md +++ b/docs/core/whats-new/dotnet-10/overview.md @@ -75,7 +75,7 @@ The F# updates in .NET 10 include several new features and improvements across t General improvements and bug fixes in the compiler implementation. -For more information, see the [F# release notes](https://fsharp.github.io/fsharp-compiler-docs/release-notes/About.html). +For more information, see [What's new in F# 10](../../../fsharp/whats-new/fsharp-10.md) or the [F# release notes](https://fsharp.github.io/fsharp-compiler-docs/release-notes/About.html). ## Visual Basic From a1b5f30b5cbe07599514596eeb16b34f73c1a22a Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 7 Nov 2025 09:27:55 -0500 Subject: [PATCH 4/7] Apply suggestions from code review Co-authored-by: Adam Boniecki <20281641+abonie@users.noreply.github.com> --- docs/fsharp/whats-new/fsharp-10.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/fsharp/whats-new/fsharp-10.md b/docs/fsharp/whats-new/fsharp-10.md index 2b0f95f4247e2..d0ecec26fd8ca 100644 --- a/docs/fsharp/whats-new/fsharp-10.md +++ b/docs/fsharp/whats-new/fsharp-10.md @@ -9,7 +9,7 @@ ai-usage: ai-assisted F# 10 brings you several improvements to the F# language, FSharp.Core library, and tooling. This version is a refinement release focused on clarity, consistency, and performance, with small but meaningful improvements that make your everyday code more legible and robust. -F# 10 ships with **.NET 10** and **Visual Studio 2022**. +F# 10 ships with **.NET 10** and **Visual Studio 2026**. You can download the latest .NET SDK from the [.NET downloads page](https://dotnet.microsoft.com/download). @@ -379,4 +379,4 @@ If you want to opt out while still enjoying other preview features, set `Paralle ``` -Parallel compilation can significantly reduce compilation times for projects with multiple files and dependencies. \ No newline at end of file +Parallel compilation can significantly reduce compilation times for projects with multiple files and dependencies. From 4636e0cd89b11e4bb5a43b8c76b8e859254ccf4b Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Fri, 7 Nov 2025 16:03:23 +0000 Subject: [PATCH 5/7] Add links to language reference --- docs/fsharp/whats-new/fsharp-10.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/fsharp/whats-new/fsharp-10.md b/docs/fsharp/whats-new/fsharp-10.md index d0ecec26fd8ca..6bb39eb7fa7b2 100644 --- a/docs/fsharp/whats-new/fsharp-10.md +++ b/docs/fsharp/whats-new/fsharp-10.md @@ -21,7 +21,7 @@ For more information, see [Get started with F#](../get-started/index.md). ## Scoped warning suppression You can now suppress warnings in specific sections of your code using the new `#warnon` directive. -This pairs with the existing `#nowarn` directive to give you precise control over which warnings apply where. +This pairs with the [existing `#nowarn` directive](../language-reference/compiler-directives.md#warn-directives) to give you precise control over which warnings apply where. Previously, when you used `#nowarn`, it would disable a warning for the remainder of the file, which could suppress legitimate issues elsewhere. Let's look at a motivating example: @@ -64,7 +64,7 @@ This feature implements [RFC FS-1146](https://github.com/fsharp/fslang-design/bl ## Access modifiers on auto property accessors A common pattern in object-oriented programming is to create publicly readable but privately mutable state. -Before F# 10, you needed explicit property syntax with backing fields (hidden variables that store the actual property values) to achieve this, which added repetitive code: +Before F# 10, you needed explicit [property syntax](../language-reference/members/properties.md) with backing fields (hidden variables that store the actual property values) to achieve this, which added repetitive code: ```fsharp type Ledger() = @@ -95,7 +95,7 @@ This feature implements [RFC FS-1141](https://github.com/fsharp/fslang-design/bl ## ValueOption optional parameters -You can now use a struct-based `ValueOption<'T>` representation for optional parameters. +You can now use a struct-based [`ValueOption<'T>`](../language-reference/value-options.md) representation for [optional parameters](../language-reference/parameters-and-arguments.md#optional-parameters-f-native). When you apply the `[]` attribute to an optional parameter, the compiler uses `ValueOption<'T>` instead of the reference-based `option` type. This avoids heap allocations (memory allocated on the managed heap that requires garbage collection) for the option wrapper, which is beneficial in performance-critical code. @@ -128,7 +128,7 @@ This feature strengthens parity with other F# language constructs that already s ## Tail-call support in computation expressions -F# 10 adds tail-call optimizations for computation expressions. +F# 10 adds [tail-call](../language-reference/functions/recursive-functions-the-rec-keyword.md#tail-recursion) optimizations for [computation expressions](../language-reference/computation-expressions.md). Computation-expression builders can now opt into these optimizations by implementing special methods. When the compiler translates computation expressions into regular F# code (a process called desugaring), it recognizes when an expression like `return!`, `yield!`, or `do!` appears in a tail position. @@ -297,7 +297,7 @@ The early errors prevent test discovery problems and ensure that attributes like ## Support for `and!` in task expressions -You can now await multiple tasks concurrently using `and!` in task expressions. +You can now await multiple tasks concurrently using [`and!`](../language-reference/computation-expressions.md#and) in [task expressions](../language-reference/task-expressions.md). Using `task` is a popular way to work with asynchronous workflows in F#, especially when you need interoperability with C#. However, until now there was no concise way to await multiple tasks concurrently in a computation expression. From 919698f4f18ca8219f5fa79746b3d3f4606d286f Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Fri, 7 Nov 2025 16:14:18 +0000 Subject: [PATCH 6/7] Add entries about *Final methods to CE reference --- docs/fsharp/language-reference/computation-expressions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/fsharp/language-reference/computation-expressions.md b/docs/fsharp/language-reference/computation-expressions.md index 75d668b36d595..f3224cae1fcd4 100644 --- a/docs/fsharp/language-reference/computation-expressions.md +++ b/docs/fsharp/language-reference/computation-expressions.md @@ -264,6 +264,7 @@ The following table describes methods that can be used in a workflow builder cla |`Delay`|`(unit -> M<'T>) -> Delayed<'T>`|Wraps a computation expression as a function. `Delayed<'T>` can be any type, commonly `M<'T>` or `unit -> M<'T>` are used. The default implementation returns a `M<'T>`.| |`Return`|`'T -> M<'T>`|Called for `return` in computation expressions.| |`ReturnFrom`|`M<'T> -> M<'T>`|Called for `return!` in computation expressions.| +|`ReturnFromFinal`|`M<'T> -> M<'T>`|If present, called for `return!` and `do!` when in tail-call position.| |`BindReturn`|`(M<'T1> * ('T1 -> 'T2)) -> M<'T2>`|Called for an efficient `let! ... return` in computation expressions.| |`BindNReturn`|`(M<'T1> * M<'T2> * ... * M<'TN> * ('T1 * 'T2 ... * 'TN -> M<'U>)) -> M<'U>`|Called for efficient `let! ... and! ... return` in computation expressions without merging inputs.

for example, `Bind3Return`, `Bind4Return`.| |`MergeSources`|`(M<'T1> * M<'T2>) -> M<'T1 * 'T2>`|Called for `and!` in computation expressions.| @@ -277,6 +278,7 @@ The following table describes methods that can be used in a workflow builder cla |`While`|`(unit -> bool) * Delayed<'T> -> M<'T>`or

`(unit -> bool) * Delayed -> M`|Called for `while...do` expressions in computation expressions.| |`Yield`|`'T -> M<'T>`|Called for `yield` expressions in computation expressions.| |`YieldFrom`|`M<'T> -> M<'T>`|Called for `yield!` expressions in computation expressions.| +|`YieldFromFinal`|`M<'T> -> M<'T>`|If present, called for `yield!` when in tail-call position and in case of `do!` in tail-call position as a fallback for `ReturnFromFinal`| |`Zero`|`unit -> M<'T>`|Called for empty `else` branches of `if...then` expressions in computation expressions.| |`Quote`|`Quotations.Expr<'T> -> Quotations.Expr<'T>`|Indicates that the computation expression is passed to the `Run` member as a quotation. It translates all instances of a computation into a quotation.| From 6aefbc356835b0bce2b30ee4bdbae399133e421a Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Fri, 7 Nov 2025 16:53:03 +0000 Subject: [PATCH 7/7] Update parameters page with voption for optional param info --- .../language-reference/parameters-and-arguments.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/fsharp/language-reference/parameters-and-arguments.md b/docs/fsharp/language-reference/parameters-and-arguments.md index 7d0cf9bce6a2a..bc99510c0611a 100644 --- a/docs/fsharp/language-reference/parameters-and-arguments.md +++ b/docs/fsharp/language-reference/parameters-and-arguments.md @@ -150,6 +150,18 @@ Baud Rate: 9600 Duplex: Full Parity: false Baud Rate: 4800 Duplex: Half Parity: false ``` +You can also specify an optional parameter to be a [value option](./value-options.md) type by applying a `[]` attribute to it. + +```fsharp +type T() = + static member M([] ?p : string) = + match p with + | ValueSome s -> printfn "%s" s + | ValueNone -> printfn "None" +``` + +When using struct-backed optional parameter, as seen above, you would use `defaultValueArg` instead of `defaultArg` to set the default value of the parameter. + ### Optional parameters (C# interop) For the purposes of C# interop, you can use the attributes `[]` in F#, so that callers will see an argument as optional. This is equivalent to defining the argument as optional in C# as in `MyMethod(int i = 3)`. This form was introduced in F# 4.1 to help facilitate interoperation with C# code.