Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion release-notes/11.0/preview/rc2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Browse the .NET 11 RC 2 release notes by component:
## Languages

- C#
- F#
- [F#](./fsharp.md)

## Workloads, Libraries, & More

Expand Down
48 changes: 48 additions & 0 deletions release-notes/11.0/preview/rc2/fsharp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# F# in .NET 11 RC 2 - Release Notes

- [Bound concurrent asynchronous work](#bound-concurrent-asynchronous-work)
- [Require named arguments for selected APIs](#require-named-arguments-for-selected-apis)
- [Reraise from a computation expression handler](#reraise-from-a-computation-expression-handler)

## Bound concurrent asynchronous work

`Async.parallelLimit` runs a sequence of async computations with a specified maximum number in flight and returns their results in input order. This helps control how much work a batch starts at once without writing a concurrency limiter yourself. Related helpers include `Async.parallelDoLimit`, `Task.parallelLimit`, and sequential variants ([dotnet/fsharp #20294](https://github.com/dotnet/fsharp/pull/20294)).

```fsharp
let results =
[ for i in 1..5 -> async { return i * i } ]
|> Async.parallelLimit 2
|> Async.RunSynchronously
// [| 1; 4; 9; 16; 25 |]
```

## Require named arguments for selected APIs

With `<LangVersion>preview</LangVersion>`, F# recognizes `System.Diagnostics.CodeAnalysis.RequireNamedArgumentsAttribute` on a method or constructor and reports `FS3923` when a caller passes the required arguments positionally. This lets API authors make call sites more explicit ([dotnet/fsharp #20340](https://github.com/dotnet/fsharp/pull/20340)).

The RC 2 BCL does not yet include the attribute. Define the attribute in your project or use a library that provides it before marking a member:

```fsharp
open System.Diagnostics.CodeAnalysis

type Calculator() =
[<RequireNamedArguments>]
member _.Double(value: int) = value * 2

let answer = Calculator().Double(value = 21) // 42
```

The [maintained fixture](../../samples/fsharp/README.md) includes the attribute definition and verifies the named call against the RC 2 SDK.

## Reraise from a computation expression handler

With the preview language version, `reraise ()` works inside the `with` handler of an `async`, `task`, or custom computation expression. The handler propagates the original exception with its stack trace, avoiding the loss of origin information that can result from `raise e` ([dotnet/fsharp #20405](https://github.com/dotnet/fsharp/pull/20405)).

```fsharp
let operation = async {
try
failwith "operation failed"
with _ ->
reraise ()
}
```
11 changes: 11 additions & 0 deletions release-notes/11.0/samples/fsharp/FSharp.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net11.0</TargetFramework>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="Polyfill.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
</Project>
7 changes: 7 additions & 0 deletions release-notes/11.0/samples/fsharp/Polyfill.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace System.Diagnostics.CodeAnalysis

open System

[<AttributeUsage(AttributeTargets.Method ||| AttributeTargets.Constructor)>]
type RequireNamedArgumentsAttribute() =
inherit Attribute()
50 changes: 50 additions & 0 deletions release-notes/11.0/samples/fsharp/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
open System
open System.Diagnostics.CodeAnalysis
open System.Threading
open System.Runtime.CompilerServices

let mutable active = 0
let mutable peak = 0
let gate = obj()
let computations =
[ for i in 1..5 ->
async {
let running = Interlocked.Increment &active
lock gate (fun () -> peak <- max peak running)
do! Async.Sleep 30
Interlocked.Decrement &active |> ignore
return i * i
} ]

let results = computations |> Async.parallelLimit 2 |> Async.RunSynchronously
if results <> [| 1; 4; 9; 16; 25 |] || peak > 2 || peak < 1 then
failwithf "Unexpected bounded-parallel results: %A; peak concurrency: %d" results peak
printfn "Bounded-parallel work passed (peak concurrency: %d)." peak

type Calculator() =
[<RequireNamedArguments>]
member _.Double(value: int) = value * 2

let actual = Calculator().Double(value = 21)
if actual <> 42 then failwithf "Expected 42, got %d" actual
printfn "Named argument validation passed."

let original = InvalidOperationException("original exception")

[<MethodImpl(MethodImplOptions.NoInlining)>]
let throwOriginal () = raise original

let operation = async {
try
throwOriginal ()
with _ ->
reraise ()
}

try
operation |> Async.RunSynchronously
failwith "Expected the original exception."
with ex when obj.ReferenceEquals(ex, original) ->
if not (ex.StackTrace.Contains("throwOriginal")) then
failwith "The original exception stack trace was lost."
printfn "Computation-expression reraise retained the exception and stack trace."
3 changes: 3 additions & 0 deletions release-notes/11.0/samples/fsharp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# F# release-note validation

With SDK `11.0.100-rc.2.26475.137` on `PATH`, run `.\Validate.ps1`. The fixture checks the ordered results and peak concurrency of `Async.parallelLimit 2`, and confirms that `reraise ()` in an async computation-expression handler preserves the original exception and stack trace. It also supplies `RequireNamedArgumentsAttribute` as a polyfill because this build does not ship it in the BCL, runs a marked method using a named argument, and checks that a positional call in the separate `invalid/Invalid.fsproj` reports `FS3923`.
11 changes: 11 additions & 0 deletions release-notes/11.0/samples/fsharp/Validate.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
$ErrorActionPreference = 'Stop'
$dotnet = (Get-Command dotnet -ErrorAction Stop).Source

& $dotnet run --project (Join-Path $PSScriptRoot 'FSharp.fsproj')
if ($LASTEXITCODE -ne 0) { throw 'The named-argument example did not run.' }

$diagnostics = & $dotnet build (Join-Path $PSScriptRoot 'invalid\Invalid.fsproj') --nologo 2>&1 | Out-String
if ($LASTEXITCODE -eq 0 -or $diagnostics -notmatch 'error FS3923') {
throw "Expected positional-call error FS3923, got: $diagnostics"
}
Write-Output 'Positional call correctly reported FS3923.'
7 changes: 7 additions & 0 deletions release-notes/11.0/samples/fsharp/invalid/Invalid.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
open System.Diagnostics.CodeAnalysis

type Calculator() =
[<RequireNamedArguments>]
member _.Double(value: int) = value * 2

Calculator().Double(21) |> ignore
11 changes: 11 additions & 0 deletions release-notes/11.0/samples/fsharp/invalid/Invalid.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net11.0</TargetFramework>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Polyfill.fs" />
<Compile Include="Invalid.fs" />
</ItemGroup>
</Project>
Loading