Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for C# collection expressions in F# lists and sets #1355

Closed
6 tasks done
IsaacSchemm opened this issue Mar 20, 2024 · 7 comments
Closed
6 tasks done

Support for C# collection expressions in F# lists and sets #1355

IsaacSchemm opened this issue Mar 20, 2024 · 7 comments

Comments

@IsaacSchemm
Copy link

IsaacSchemm commented Mar 20, 2024

I propose we add support for C# collection expressions to F# lists and sets, so that a collection of this type can be initialized from C# code with a collection expression.

The use case for F# lists and sets in C# code, as I see it, is to extend the immutability and value equality of C# records to those with collections of equatable items (strings, integers, other records) as properties/fields. By combining a record with an immutable collection type that (like the record) implements value equality, a complex type can be constructed that is fully immutable and supports "deep" equality checks all the way down, as is often done in F#. This becomes very useful when writing unit tests that need to define and check all fields and sub-fields on objects,. It also opens up use cases for these complex types to be used in a HashSet or as keys in a Dictionary.

C# collection expressions don't have "automatic" support for immutable collections (the compiler looks for an Add method), but support can be added to a collection type by adding a CollectionBuilderAttribute to the type and implementing a static creation method.

This code can be used today in C#:

record Poem {
    public string Name { get; init; } = "Untitled";
    public string Content { get; init; } = "";
    public FSharpSet<string> Tags { get; init; } = SetModule.Empty<string>();
}

Poem originalPoem = repository.GetPoem(id);
Poem newPoem = originalPoem with { Tags = SetModule.FromArray(["sonnet", "moon", "sky"]) };
if (originalPoem != newPoem) {
    repository.UpdatePoem(id, newPoem);
}

With this change, the code could become:

record Poem {
    public string Name { get; init; } = "Untitled";
    public string Content { get; init; } = "";
    public FSharpSet<string> Tags { get; init; } = [];
}

Poem originalPoem = repository.GetPoem(id);
Poem newPoem = originalPoem with { Tags = ["sonnet", "moon", "sky"] };
if (originalPoem != newPoem) {
    repository.UpdatePoem(id, newPoem);
}

The existing ways of approaching this problem are to:

  • use a non-F# collection type such as the ones in the namespace System.Collections.Immutable; these types do not implement value equality; so another approach must be used to compare two objects
  • initialize another collection type (with or without collection expressions) and convert to an F# list using ListModule.OfSeq or ListModule.OfArray
  • implement a custom collection type that supports value equality (with object.Equals and with IEquatable<T>) and collection expressions, as well as whatever other functions / features are needed

Pros and Cons

The advantages of making this adjustment to F# are:

  • Better interoperability with C# code that uses F#-defined types
  • Making the F# list type more useful for C# developers

The disadvantages of making this adjustment to F# are:

  • Code must be added to the F# library (and maintained) to support a C# feature that does not (currently) exist in F#

Extra information

Estimated cost (XS, S, M, L, XL, XXL): XS

Related suggestions:

Affidavit (please submit!)

Please tick these items by placing a cross in the box:

  • This is not a question (e.g. like one you might ask on StackOverflow) and I have searched StackOverflow for discussions of this issue
  • This is a language change and not purely a tooling change (e.g. compiler bug, editor support, warning/error messages, new warning, non-breaking optimisation) belonging to the compiler and tooling repository
  • This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it
  • I have searched both open and closed suggestions on this site and believe this is not a duplicate

Please tick all that apply:

  • This is not a breaking change to the F# language design
  • I or my company would be willing to help implement and/or test this
@Happypig375
Copy link
Contributor

Happypig375 commented Mar 20, 2024

Do you mean F# should define these methods for C# consumption? Should this relate to #969 for usage in F# too?

@dsyme
Copy link
Collaborator

dsyme commented Mar 20, 2024

Marked as approved in principle!

@IsaacSchemm
Copy link
Author

@Happypig375 I'll add #969 as related - it looks like if F# adds the ability to consume CollectionBuilderAttribute, it would be tracked as part of that issue. Right now this would just be for C# consumption.

@xperiandri
Copy link

This is my favorite feature since C# added async and await. I would love to see it appear in F# ASAP

@abelbraaksma
Copy link
Member

abelbraaksma commented May 28, 2024

@xperiandri are you interested in writing an RFC for this and/or a PR to further the proposal?

@vzarytovskii
Copy link

To make sure everyone understands what it covers. It doesn't cover having "one syntax" for all collections in F#, but make C# "understand" out collections.

@brianrourkeboll
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants