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

NonEmpty from C# #45

Open
ntwilson opened this issue May 21, 2019 · 4 comments
Open

NonEmpty from C# #45

ntwilson opened this issue May 21, 2019 · 4 comments

Comments

@ntwilson
Copy link
Owner

Currently, it's prohibitively difficult to use any NonEmpty collections from C#. Do we have a plan to address this? This issue isn't really proposing anything so much as opening for discussion.

@ntwilson
Copy link
Owner Author

ntwilson commented May 21, 2019

The problem, btw, is that NonEmpty<,> inherits from IEnumerable (as it should), but LINQ already defines a bunch of partial functions for IEnumerables, and you can't make them go away unless you just don't add using System.Linq; at the top of your file. So whether a list is NonEmpty or not, you can still call .First() on it.
It seems like from C# we don't have a good way of taking advantage of the library building up knowledge of what operations are allowed specifically on non empty collections like we can in F# by seeing what operations are available from the NonEmpty module (and by using SafetyFirst.Strict). BUT we should still be able to take in any kind of NonEmpty collection as an input to a function to communicate assumptions, and then just call .First() on it like any other IEnumerable.

@ntwilson
Copy link
Owner Author

ntwilson commented May 27, 2019

I think this involes:

  1. A .Match(empty: () => ..., notEmpty: nes => ...) extension method for IEnumerables
  2. An .AsNonEmpty() : Option<NonEmptySeq<T>> extension method for IEnumerables
  3. An .AssumeNonEmpty() : NonEmptySeq<T> extension method for IEnumerables. Since we can't have functionality specific to NonEmptySeqs from C#, there would probably be multiple instances where the programmer is confident that a seq is NonEmpty, but there's no good way to make the compiler prove that, so it would be appropriate to give them a way to assert it to the compiler.
  4. Is there a way to make the type signature cleaner without aliases? Writing out NonEmpty<IEnumerable<int>, int> would be really annoying. NonEmpty is implemented as a single case union, so I don't think that making e.g., a NonEmptyList class that inherits from NonEmpty is an option. We could take the route of making e separate set of types for C# and F#, and adding methods to convert between them for interop...

@ntwilson
Copy link
Owner Author

ntwilson commented May 27, 2019

we could also make a new single case union or other struct for each seq type, e.g., type NonEmptyList<'a> = NonEmptyList of NonEmpty<ResizeArray<'a>, 'a>
We'd probably want to throw that in some sort of C# specific namespace though to avoid interfering with the default aliases. Does that mean we'd want to split the NonEmpty stuff out into separate .CSharp and .FSharp namespaces? That would certainly make this a breaking change, and more frustrating for F# users.

@ntwilson
Copy link
Owner Author

ntwilson commented May 27, 2019

Perhaps since C# really would only need a single non empty type, we could just keep it in the same namespace and make

[<struct>] 
type NonEmptyCollection<'T> private (xs:'T seq) = 
  static member Verify xs = 
    if Seq.isEmpty xs then None else Some (NonEmptyCollection xs)
  interface IEnumerable<'T> with
    member this.GetEnumerator () = xs.GetEnumerator ()
  interface IEnumerable with
    member this.GetEnumerator () : IEnumerator = upcast (xs.GetEnumerator ())

Is it an issue that this type would have no relation to the F# NonEmpty types? I'm thinking as long as we have a way to convert from NonEmptySeq<'a> to NonEmptyCollection<'T> and vice versa, we'd be okay...

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

1 participant