Skip to content

Comments

Change Seq.empty to use Enumerable.Empty#19322

Closed
bbatsov wants to merge 1 commit intodotnet:mainfrom
bbatsov:seq-empty-enumerable-empty
Closed

Change Seq.empty to use Enumerable.Empty#19322
bbatsov wants to merge 1 commit intodotnet:mainfrom
bbatsov:seq-empty-enumerable-empty

Conversation

@bbatsov
Copy link
Contributor

@bbatsov bbatsov commented Feb 18, 2026

Description

Seq.empty was implemented as a single-case DU (EmptyEnumerable) that carries a [CompilationMapping(SourceConstructFlags.SumType)] attribute. When serializers (JSON.NET, STJ, ASP.NET ObjectResult) inspect the runtime type via reflection, they detect this attribute and treat it as an F# union rather than a plain enumerable, causing Seq.empty to serialize as "EmptyEnumerable" instead of [] or throw NotSupportedException.

Replacing the implementation with System.Linq.Enumerable.Empty<'T>() fixes this — the runtime type is now a standard .NET type that serializers handle correctly. The internal EmptyEnumerable DU and a related type-check optimization in ConcatEnumerator are no longer needed and have been removed.

I basically did what was suggested in the ticket, so I hope I got it right. It's fun to hunt for simple issue that I might be able to help with.

Fixes #17864

Checklist

  • Test cases added
  • Performance benchmarks added in case of performance changes
  • Release notes entry updated

@github-actions
Copy link
Contributor

github-actions bot commented Feb 18, 2026

❗ Release notes required


✅ Found changes and release notes in following paths:

Change path Release notes path Description
src/FSharp.Core docs/release-notes/.FSharp.Core/10.0.300.md

Seq.empty was implemented as a single-case DU (EmptyEnumerable) which
caused serializers to detect the CompilationMapping attribute and treat
it as an F# union type instead of a plain enumerable. This made
Seq.empty serialize as "EmptyEnumerable" instead of [] with JSON.NET,
STJ, and ASP.NET ObjectResult.

Replace the implementation with System.Linq.Enumerable.Empty<'T>(),
which returns a standard .NET type that serializes correctly. Also
remove the EmptyEnumerable type and the related optimization in
ConcatEnumerator that is no longer needed.

Fixes dotnet#17864
@bbatsov bbatsov force-pushed the seq-empty-enumerable-empty branch from fd7fb57 to 20f995d Compare February 18, 2026 09:03
@T-Gro
Copy link
Member

T-Gro commented Feb 18, 2026

Writing here as well as to https://github.com/dotnet/fsharp/pull/19317/changes .

Even though this change is correct, we should first assess breaking changes even for de-generate scenarios, only merge this for upcoming major increase of FSharp.Core (together with .NET 11. Right now we still dually insert also to 10.0.300) and document the breaks.

Few ideas:

  • We have to cross compat test suite the builds programs in various combinations of old/new compiler for a lib/app scenario
  • Does this change = or physical equality in any way?
    • How does it change when we consider a NET10 lib (its seq.empty) and a NET11 app (its seq.empty), will this change physical equality?
    • Both PRs remove the type, what happens when a library uses seq.empty, an app imports it, and the app brings in a newer version of FSharp.Core ?

Imo the compiler compat suite will allow to model these scenarios, the infrastructure around it will then exercise the various versioning configs that can happen.

@bbatsov
Copy link
Contributor Author

bbatsov commented Feb 19, 2026

Closing in favor of #19317, which fixes the serialization issue without removing EmptyEnumerable<'T>, avoiding any breaking changes. I guess I wasn't the only one looking for good first issues and I guess his approach is better.

Unfortunately I still have a lot to learn about F#'s internals.

@bbatsov bbatsov closed this Feb 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Seq.empty renders as "EmptyEnumerable"

2 participants