In LINQ we have the Single
and SingleOrDefault
methods, but they don't tell you if it succeeded or not.
The problem with these methods is that if there is more than one element in source
(or more than one matching element in case of predicate
) they all throw a System.InvalidOperationException
. That is fine if more than one element is an error or very rare. If more than one element is common or expected then this will cause major slow downs.
Additionaly, in cases of (example: int collections), default(int) is 0, which wouldn't tell the caller whether the operation succeeded (returning the item 0) or not (returning the default 0).
namespace System.Linq
{
public static class Enumerable
{
public static TSource Single<TSource>(this IEnumerable<TSource> source);
public static TSource Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source);
public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
+ public static bool TrySingle<TSource>(this IEnumerable<TSource> source, out TSource element);
+ public static bool TrySingle<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out TSource element);
public static TSource First<TSource>(this IEnumerable<TSource> source);
public static TSource First<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source);
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
+ public static bool TryFirst<TSource>(this IEnumerable<TSource> source, out TSource element);
+ public static bool TryFirst<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out TSource element);
public static TSource Last<TSource>(this IEnumerable<TSource> source);
public static TSource Last<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source);
public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
+ public static bool TryLast<TSource>(this IEnumerable<TSource> source, out TSource element);
+ public static bool TryLast<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out TSource element);
public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index);
public static TSource ElementAtOrDefault<TSource>(this IEnumerable<TSource> source, int index);
+ public static bool TryElementAt<TSource>(this IEnumerable<TSource> source, int index, out TSource element);
}
public static class Queryable
{
public static TSource Single<TSource>(this IQueryable<TSource> source);
public static TSource Single<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static TSource SingleOrDefault<TSource>(this IQueryable<TSource> source);
public static TSource SingleOrDefault<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
+ public static (bool success, T value) TrySingle<T>(this IQueryable<T> source);
+ public static (bool success, T value) TrySingle<T>(this IQueryable<T> source, Expression<Func<TSource, bool>> predicate);
+ public static bool TrySingle<TSource>(this IQueryable<TSource> source, out TSource element);
+ public static bool TrySingle<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, out TSource element);
public static TSource First<TSource>(this IQueryable<TSource> source);
public static TSource First<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static TSource FirstOrDefault<TSource>(this IQueryable<TSource> source);
public static TSource FirstOrDefault<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
+ public static (bool success, T value) TryFirst<T>(this IQueryable<T> source);
+ public static (bool success, T value) TryFirst<T>(this IQueryable<T> source, Expression<Func<TSource, bool>> predicate);
+ public static bool TryFirst<TSource>(this IQueryable<TSource> source, out TSource element);
+ public static bool TryFirst<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, out TSource element);
public static TSource Last<TSource>(this IQueryable<TSource> source);
public static TSource Last<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static TSource LastOrDefault<TSource>(this IQueryable<TSource> source);
public static TSource LastOrDefault<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
+ public static (bool success, T value) TryLast<T>(this IQueryable<T> source);
+ public static (bool success, T value) TryLast<T>(this IQueryable<T> source, Expression<Func<TSource, bool>> predicate);
+ public static bool TryLast<TSource>(this IQueryable<TSource> source, out TSource element);
+ public static bool TryLast<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, out TSource element);
public static TSource ElementAt<TSource>(this IQueryable<TSource> source, int index);
public static TSource ElementAtOrDefault<TSource>(this IQueryable<TSource> source, int index);
+ public static (bool success, T value) TryElementAt<T>(this IQueryable<T> source, int index);
+ public static bool TryElementAt<TSource>(this IQueryable<TSource> source, int index, out TSource element);
}
}
I'd like to propose a new LINQ method: TrySingle
. The idea is pretty much the same as the existing Single
method except it does not throw an exception when there is not only a single item in the sequence.
To do this I made to modifications to the Single
contract:
bool
. true
if the sequence contains exactly one item, false
otherwise.TSource
. This will "hold" the single element on success. On failure it is filled with default(TSource)
.I have found this method very useful in my own code and if accepted can quickly put together a pull request.
If one was going to do this, I would see it as cleaner if all of the methods with XxxOrDefault()
had a TryXXX
.
I do see what you're getting at but I'm not "replacing" the SingleOrDefault()
method, since that still throws an exception.
I'd say any API additions with the Try
pattern should be held off until the Tuple design for C# 7 is finalized. It might be better for these types of methods to return tuples.
I actually like the way this works because it makes using this method in a if
statement very easy:
int I;
if (seq.TrySingle(out I))
{
...
}
(I admit though that TryXXX
might not be the best name for this method though)
I wouldn't suggest any of them as a replacement (I'd argue vehemently against), but rather just as one might say "I want to get the single matching element here, or if there isn't a single matching element, know that I failed to get it" one might also say "I want to get the first matching element here, or if there isn't one, know that I failed to get it" and so on.
FWIW, some code I'm playing with now has a TryGetFirst
, TryGetLast
and TryGetElementAt
because it became necessary to distinguish from default(TSource)
of a failure and of the element happening to be the default.
Ah yes, for Last
, First
, and ElementAt
a TryXXX
method would be great. The issue here is that I'm trying to avoid an exception which event the xxxOrDefault
doesn't do. My use of the TryXXX
naming may not have been the best choice.
Yep. Much of the time the SingleOrDefault
would work, but if the default value is a possible real value, then it's no good to you.
How would SingleOrDefault
work? My case is that having more than one item is common and that means both Single
and SingleOrDefault
throw an exception, something I really don't want happening! (SingleOrDefault
returns default on no items)
Ah. I get your meaning now. Single of course has two failure modes...
Maybe a name other than TrySingle
?
I wonder if a caller might want to distinguish failure because there are none from failure because there are more than one?
So say return an enum?
enum SequenceLength
{
Empty,
Single,
More
}
After the discussion above, some more thinking myself, and looking at the contributing guidelines...
In LINQ we have the Single
and SingleOrDefault
methods:
namespace System.Linq
{
public static class Enumerable
{
public static TSource Single<TSource>(this IEnumerable<TSource> source);
public static TSource Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source);
public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
}
}
The problem with these methods is that if there is more than one element in source
(or more than one matching element in case of predicate
) they all throw a System.InvalidOperationException
. That is fine if more than one element is an error or very rare. If more than one element is common or expected then this will cause major slow downs.
I propose the following methods be added:
namespace System.Linq
{
public static class Enumerable
{
public static bool Single<TSource>(this IEnumerable<TSource> source, out TSource element);
public static bool Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out TSource element);
}
}
public static bool Single<TSource>(this IEnumerable<TSource> source, out TSource element);
1) If source
is null
, throw System.ArgumentNullException
.
2) If source
is empty or has more than one element, set element
to default(TSource)
and return false
.
3) If source
has one element, set element
to that element and return true
.
public static bool Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out TSource element);
1) If source
or predicate
is null
, throw System.ArgumentNullException
.
2) If source
has no elements or has no elements that predicate
returns true
for or has more than one element that predicate
returns true
for, set element
to default(TSource)
and return false
.
3) If source
has one element that predicate
returns true
for, set element
to that element and return true
.
As to @JonHanna 's point about wanting to know more information, I feel that would be a whole new method.
...if more than one element is common/expected, then you have one of two situations:
First(...)
would be appropriate.Single(...)
is appropriate."Exceptions are slow" is irrelevant in the case of requesting user input to resolve the issue - displaying to the screen and acting on the input is going to take far longer.
If you're planning on doing something like showing a pop-up with multiple results (if they exist) for selection, you're going to have to get them regardless. At that point, it'd be better to start with the results of a Where(...)
query and go from there.
What was the situation you were planning on using this method to solve?
My use case is a user is changing selection. They can:
Hmm, wouldn't that be approached as:
I mean, in the case where the user has multiple items selected and attempts your processing, you can't actually do nothing; you need to at least let them know you can't continue.
For doing things like greying out UI buttons based on number of selected items, you probably want to be using Count(...)
.
The processing is automatic and there are multiple "handlers" for selection. Some handle more than one item, this one doesn't. So I do silently do nothing. I could use Count
but I don't care beyond 2, so it would be wasting cycles counting every item
@VSadov says this is useful addition.
Without reading it all, we think TrySingle
is better name. Updating proposal on the top.
Should I create a PR based on my implementation?
Not yet, we need to do API review (scheduled every Tuesday at 10am PT).
We will likely require all Try* APIs for completness (final decision hopefully tomorrow).
We'll mark it up for grabs and assign it to you @SamuelEnglard when it is ready for implementation.
Makes sense. And easy enough to do.
@karelz updated the proposal.
I prefer to solve this with T? SingleOrNull<T>(this IEnumerable<T> source) where T : struct
.
I have SingleOrNull, FirstOrNull, MaxOrNull, SumOrNull, IReadOnlyDictionary.GetValueOrNull, etc.
They are all shorthand for .AsNullable().SingleOrDefault()
, etc.
This itself is shorthand for .Select(_ => (T?)_).SingleOrDefault()
.
These are all for value types. For ref types, *OrNull
becomes *OrDefault
.
@jnm2 are you proposing changes to the API shape? Or just mentioning an alternative in general?
Personally I think the Try* pattern is already well established in BCL and easier to use / comprehend.
Also, *OrDeault
does not work well with cases when null
is acceptable value.
@karelz Just making sure the concept gets discussed before the API is finalized. As you pointed out, it doesn't help in cases where null
is acceptable. On that basis I don't think the API shape should be changed.
Approved as is in the top most post.
@karelz I just got cc'd from @AlexRadch's PR at https://github.com/dotnet/corefx/pull/14276. I know this is the second time I've done this today, but can the naming of this API be reconsidered?
I wholly understand and agree with adding this API. However, as a contributor to Linq, I feel rather strongly that TryGetSingle
would be a better name. Virtually all of the Try*
methods-- TryGetValue
, TryAdd
, etc-- have a verb after the Try
. This translates in my mind as "try getting this value/adding this item, and return whether we succeeded." TrySingle
is a verb + a noun, which does not translate smoothly in my mind at all. Same for TryFirst
, TryLast
, and TryElementAt
. I strongly suggest that the naming be reconsidered, even if it is 3 extra characters. @SamuelEnglard and others, do you disagree?
I agree on Add
. The ideal would be if there was an appropriate adjective to match most of the similar methods, but there isn't. The To…
form isn't appropriate, so really TryGet…
seems the best option here.
@karelz I can rename these methods to TryGet
. Is it approved?
These also make sense to include on Queryable
.
I do agree with @jamesqo point, though I think the missing "Get" is really with the original methods to start with.
Well, the descriptive quality of the adjectives makes sense on their own, but when the verb Try
is introduced they become stranger. Or at least now that @jamesqo pointed it out they seem strange!
On the Queryable
matter, we're keeping the parity with SkipLast
and TakeLast
and perhaps (still being discussed) restoring it for Prepend
and Append
. I think it definitely makes sense here too.
As I can see Queryable
does not have any methods with out
parameter. Is it possible to have such methods in Queryable
?
We already discussed TryGetFirst
vs. TryFirst
naming - see https://github.com/dotnet/corefx/issues/6073#issuecomment-263421263. I think of it more as pattern of adding Try
prefix before the method name.
Queryable
companion methods sound ok to me - @OmarTawfik can you please confirm? (@VSadov is OOF for another week I think).
@SamuelEnglard I have to apologize - I didn't follow up on my promise of assigning the issue to you (https://github.com/dotnet/corefx/issues/6073#issuecomment-263437700). It was by accident.
@AlexRadch that's another reason why it would be great if you can please ping each issue before you grab it in future. It will be easier to organize who works on what - avoiding duplicated efforts, "stolen features", etc. Thank you!
@karelz sorry. I did not see this comment.
That makes two of us ;-) -- I think it is as a good motivation to ping the issue in future, before you start working on it :)
@karelz it's cool. I've literally have the code sitting on my machine, just haven't had the time to "move" it to my fork to push up so no time lost on my end
We already discussed TryGetFirst vs. TryFirst naming - see dotnet/corefx#6073 (comment). I think of it more as pattern of adding Try prefix before the method name.
We didn't really discuss it as much as close over it.
We didn't really discuss it as much as close over it.
:+1:
@karelz, to give you a better sense of how I am thinking about this, it is like in English saying "try train" instead of "try catch train" or "try car" instead of "try stop car". I think it would make much more sense with a verb.
Grammatically, I agree that TryGetSingle
is best. With TrySingle
, You're not "trying to single" (single as a verb can only be used transitively) and you're not "trying a single" (the trying is being applied to the sequence, not to the result).
You can sacrifice grammatical sense for terseness, but in this case it just sounds awkward.
Also we have TryGetValue
on dictionaries. We should stick with existing standards.
Using current spec but WIP https://github.com/SamuelEnglard/corefx/blob/TrySingle-Issue6073/src/System.Linq/src/System/Linq/Single.cs
We didn't really discuss it as much as close over it.
What I meant we discussed it briefly in triage with @VSadov and @OmarTawfik. It was raised here (https://github.com/dotnet/corefx/issues/6073#issuecomment-183438071) by @JonHanna, but it didn't spark deeper discussion here.
Moreover, in the API review (with English native speakers), no one raised it either.
I personally believe that having the name closer to Single
method name (BTW: was it a mistake of the past to name it GetSingle
in the first place?) is better for discoverability of the API and to create connection between the 2 APIs.
But to be honest, I don't care too much what we choose + I am ESL, so I wouldn't push too hard for my opinion anyway ;-).
@terrajobst @danmosemsft @bartonjs @KrzysztofCwalina @stephentoub @weshaggard can you please vote on the name choice TrySingle
vs. TryGetSingle
? Do we have guidelines? Do we care? (Proper English grammar TryGetSingle
vs. using Try
as prefix to existing method names - TrySingle
)
can you please vote on the name choice TrySingle vs. TryGetSingle?
If Single were instead GetSingle and SingleOrDefault were instead GetSingleOrDefault, then TryGetSingle would make sense. But they're not. The Try pattern is generally to simply prefix the operation with "Try" and have it return a bool with the actual result as an out parameter, so it seems we should do that here as well. Thus I vote for TrySingle. e.TrySingle is no more strange than e.Single, and there's value in having them be consistent with each other, more so I believe than breaking from the pattern and trying to clarify the name.
I concur with Steve. The original mistake was having a method with no verb, but we should stick to the naming scheme of just prepending "Try". Because the breakdown is "try that other method... return false if it would have thrown an exception had I called it". Aka
try { ret = Single(); return true; } catch { return false; }
(And, yeah, Karel, I'm agreeing with you, too, but you mentioned both sides and asked a question, Steve only answered :smile:)
@stephentoub @bartonjs I generally think of Linq's extension methods as properties rather than methods. First()
in my mind is not performing an action, but retrieving a trivially obtainable aspect of the enumerable. However, with Try
the operation is no longer trivial because it can potentially fail. So my mind converts Single
(an aspect) -> GetSingle()
(an action) -> TryGetSingle()
(an action that might fail).
Having said that, I do not want to block this proposal since there is already a PR for it. Plus @bartonjs' comment clarifies things a bit more. I guess having TryX
would be OK.
@jamesqo all the methods we're adding TryX
to had the potential to fail in the first place.
@karelz Can this be API reviewed again tomorrow for the corresponding methods to be added to Queryable
? All of the TryFirst
, TryLast
, TryElementAt
, and TrySingle
overloads mentioned in the description?
@jamesqo There is no solution how to add TryX methods to Queryable.
@AlexRadch I only glanced at his comments, but I think @bartdesmet mentioned some possible solutions at https://github.com/dotnet/corefx/pull/14276#discussion_r91574861? It seems like just he's waiting for input from @divega about which is the best method.
@jamesqo As I understand, all possible solutions have many issues.
@jamesqo I need first comment from area experts - @OmarTawfik or @VSadov (who is OOF).
I agree with @stephentoub. I think that we should stick to the pattern of other helpers. I don't see it as Try+Verb+Noun
, but as Try+OriginalOperationName
.
If Single were instead GetSingle and SingleOrDefault were instead GetSingleOrDefault, then TryGetSingle would make sense. But they're not. The Try pattern is generally to simply prefix the operation with "Try" and have it return a bool with the actual result as an out parameter, so it seems we should do that here as well. Thus I vote for TrySingle. e.TrySingle is no more strange than e.Single, and there's value in having them be consistent with each other, more so I believe than breaking from the pattern and trying to clarify the name.
@OmarTawfik that has been already "decided". The last open question is -- should we add the Queryable
companions or not?
@karelz that makes sense to me.
Are they direct copy of the Enumerable methods? Can you please update the proposal at the top?
I am only now catching up with this discussion and I would still like to provide my input:
I can second many of @bartdesmet's comments here, and I agree with the reasoning that if we are going to have these methods on IQueryable
the version that returns a Tuple
(or ValueTuple
) seems to be more appropriate that one that uses and output parameter.
But in fact I would also argue that methods that return a Tuple
(or ValueTuple
) are a better fit for LINQ's fluent compositional style in general, even on IEnumerable<T>
.
The bool TryGetX(..., out x)
pattern is and has always been a little awkward to use and kind of the opposite of the style used in LINQ. AFAIR there have been multiple attempts to extend the language to make the former easier/more natural and support for tuple syntax is one of those features.
It might help the whole discussion if we could arrange examples of how the "success" value returned by these operaros can be used when the operators appear nested in a query in both standard method and query comprehension syntax.
@divega
But in fact I would also argue that methods that return a
Tuple
(orValueTuple
) are a better fit for LINQ's fluent compositional style in general, even onIEnumerable<T>
.
I agree. The best thing for these methods to do, actually, would be to return a T?
; it's too bad that Nullable has an artificial struct constraint. Out parameters don't really mix well with other methods because Linq is all about lazy evaluation, even though it works in this case (First
, Single
, etc. are eagerly evaluated).
On the other hand, however, Try
with out is more natural than using a ValueTuple
when working with if statements:
if (e.TryGetFirst(out var first)) { ... }
(bool got, T first) = e.TryGetFirst();
if (got) { ... }
So unless someone has a better idea, it seems like out
is the best choice we have right now.
I would like to use methods with out parameters in public interface, and at the same time, I support to use tuples inside Queryable and providers . I think this is the most convenient solution. The only issue is how find corresponding methods in Enumerable. I see next ways to solve this issue:
public static class QueryableToEnumerableHelper // or some other name
{
public Tuple<bool, T> TrySingle<T>(IEnumerable<T> source)
{
T item;
bool result = Enumerable.TrySingle(source, out item);
return Tuple.Create(result, item);
}
}
Disadvantage: we should search enumerable methods not only in Enumerable class but in QueryableToEnumerableHelper class also.
[EditorBrowsable(EditorBrowsableState.Never)]
attribute. It is like previous solution but helper methods are inside Enumerable class, not inside separated helper class. Disadvantage: user can use such methods.
Have internal methods in Enumerable with tuple for each method with out parameters. It is like previous solution but we need to find internal (not public) methods. Is it possible?
Define rules for Queryable to rewrite internal Tuple methods to Enumerable methods with out parameters. This solution is most complex and have many disadvantages.
I suggest to use second solution as the same universal and convenient. Now I rewrote PR code https://github.com/dotnet/corefx/pull/14276 to see how it will be with second solution.
@divega and @bartdesmet what do you think about such suggestion?
@jamesqo can next
(bool got, T first) = e.TryGetFirst();
if (got) { ... }
be rewritten as next in c# 7.0 or not?
if ((var tryFirst = e.TryGetFirst()).Item1) { tryFirst.Item2 }
But in any way it is bad code style.
@divega @bartdesmet @karelz I wrote some my design suggestions in https://github.com/dotnet/corefx/issues/6073#issuecomment-266939214
@AlexRadch I can't remember any precedent of LINQ operators that were designed to only be used at the top of a query. That is why I am still trying to understand if these operators could be used inside a query and how the usage would look like.
I also still believe there is a lot of value in having symmetry between the Enumerable
and Queryable
stories.
All in all if we cannot come up with something that works everywhere in the query and for both Enumerable
and Queryable
I would be inclined not to make these operators part of our core libraries. Applications are free to define them if they really need them (i.e. for those not so frequent cases in which the default value of TSource
is not enough to indicate failure to retrieve a value).
A simple implementation that I would hesitate to productize, but that would probably work ok in many cases for existing IQueryable<TSource>
providers looks reasonable and simple enough:
``` C#
public static bool TryFirst
IQueryable
Expression
out TSource first)
{
foreach(var element in source.Where(predicate)) // consider adding .Take(1)
{
first = element;
return true;
}
first = default(TSource);
return false;
}
Implementing a similar version of `TrySingle()` reminded me about `Single()`'s two failure modes (I see it already came up earlier in this thread) which make this one feels a bit weirder:
``` C#
public static bool TrySingle<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate,
out TSource single)
{
var firstFound = false;
single= default(TSource);
foreach (var element in source.Where(predicate)) // consider adding .Take(2)
{
if (firstFound)
{
return false;
}
else
{
single = element;
firstFound = true;
}
}
return firstFound;
}
@divega Thanks for great answer! As I see you suggest to add implementation inside Queryable and call old API for drivers. It makes sense because Try methods are not deferred.
I will made such implementation in this PR.
As I see you suggest to add implementation inside Queryable and call old API for drivers.
@AlexRadch Not at all! I did not intend to suggest that, sorry if I wasn't clear. I am saying that this is the kind of implementation application developers could include in their own applications if it really helps them save a few lines of code, and I am questioning the value of including any implementation these methods in the .NET BCL, for either IEnumerable<T>
or IQueryable<T>
@divega
(i.e. for those not so frequent cases in which the default value of TSource is not enough to indicate failure to retrieve a value)
I don't think it's so infrequent. Consider any generic method that wants to get the first value of an enumerable-- there's no way to do it smoothly and performantly with the current APIs. IMO, I don't see a good reason to block this proposal.
I do have an idea, however. Perhaps this is a bit radical, but what if we introduced an Optional<T>
type to System.Linq
that we could return from these functions? It would implement the Maybe monad and could be used in Linq query comprehensions. It would be similar to Nullable
, except without the artificial struct constraint.
namespace System.Linq
{
public struct Optional<T>
{
public Optional(T value);
public static Optional<T> Empty { get; }
public bool HasValue { get; }
public T Value { get; }
public T GetValueOrDefault();
public bool TryGetValue(out T value);
public Optional<TResult> Select<TResult>(Func<T, TResult> selector);
public Optional<TResult> SelectMany<TMiddle, TResult>(Func<T, Optional<TMiddle>> selector1, Func<T, TMiddle, Optional<TResult>> selector2);
}
}
We could then return this from the TryGet*
methods: (edit: This was meant to be Try*
. Corrected.)
public static Optional<T> TryFirst<T>(this IEnumerable<T> source);
public static Optional<T> TryLast<T>(this IEnumerable<T> source);
public static Optional<T> TrySingle<T>(this IEnumerable<T> source);
// + the predicate overloads
and use it like this
Optional<string> GetFirstString<T>(IEnumerable<object> objects)
{
// If `objects` is null, the selector is not run and Optional<string>.Empty is returned
return objects.TryFirst().Select(o => o.ToString());
}
// People who like the `out` pattern can still use it
if (ints.TryFirst().TryGetValue(out i))
{
Console.WriteLine(i);
}
else
{
Console.WriteLine("No ints found.");
}
// Compatible with query comprehensions
class Person { Optional<Person> Parent; }
Person person = ...
var greatGrandparent = from parent in person.Parent
from grandparent in parent.Parent
select grandparent.Parent;
I don't think it's so infrequent.
@jamesqo My statements were intended to be subjective :smile: I don't have the means to accurately measure how often it is necessary to discriminate between a default value and a non-existent value. I just made an educated guess that needing this is less frequent than not needing it, and I am also making the assumption that people can figure out other patterns when they actually do need it. Those patterns may be a bit more complicated than the existing methods in LINQ but they look reasonable to me, especially assuming that they are only necessary in a less common case. I also happen to not be a fan of the TryGetX(..., out x)
pattern. I know it sometimes can have advantages (especially with inline var support) but I still prefer other alternatives when available.
I do have an idea, however. Perhaps this is a bit radical, but what if we introduced an Optional
type to System.Linq that we could return from these functions?
I actually think this is a very interesting idea. In fact while going through some of the proposed designs on the whiteboard a few days ago I also thought that tuples weren't nice enough and that introducing a type that had more specific semantics would probably make sense. I was actually surprised that @bartdesmet had not mentioned this possibility already, or did he? :smile:
But then I realized that an IQueryable<T>
or IEnumerable<T>
can convey a very similar concept and so I rejected (perhaps too quickly?) the idea of introducing a new type. E.g., the result of
``` C#
source.Where(predicate).Take(1);
already conveys mostly the same concept as an `Optional<T>`, with the possible advantage that it stays in the same monad that you started on, e.g. if `source` happens to be an `IQueryable<T>` the result would also be an `IQueryable<T>` and conversely, if `source` is an `IEnumerable<T>` the result would be an `IEnumerable<T>`.
However I understand that a type like the one you are proposing (or perhaps the equivalent interface) would carry more precise semantics. E.g. an instance of `Optional<T>` could never represent a collection of multiple elements. I also suspect the type you propose could find other applications in the future, e.g.: to represent optional services on https://github.com/aspnet/DependencyInjection/, a new general API pattern that replaces `TryGetX(..., out x)`, and perhaps even as part of the [non]nullable reference types story?
Initially I was a bit worried about the possibility of a proliferation of types, but I am not sure these are real issues:
- Would we need a separate IQueryable<T> flavor o `Optional<T>` or would the `IQueryable<T>` version of the methods just return the same types as the `IEnumerable<T>` versions?
- Would we eventually need an async version of `Optional<T>` or will `[Value]Task<Optional<T>>` and `Optional<[Value]Task<T>>` be enough?
Also, this particular code snippet:
``` C#
if (ints.TryGetFirst().TryGetValue(out i))
{
Console.WriteLine(i);
}
made me think that perhaps we should consider coming up with a new naming pattern for methods that return Optional<T>
rather than overloading the existing TryGet...
naming. E.g.:
FirstAsOptional()
OptionalFirst()
Although I am not convinced of this either (I don't like any of the names I could come up with).
I would love to hear what others think about your idea.
@divega I was waiting for others to reply in the meantime, but since no one has:
I also suspect the type you propose could find other applications in the future,
I think so too; places like Dictionary.TryGetValue
would be ideal candidates.
Would we need a separate IQueryable flavor o Optional
or would the IQueryable version of the methods just return the same types as the IEnumerable versions? Would we eventually need an async version of Optional
or will [Value]Task > and Optional<[Value]Task > be enough?
I don't think there is any need to add more "flavors"-- the type should work fine with existing containers.
I would love to hear what others think about your idea.
👍 I would also like to hear what others think.
Also: If we were to pursue this, a nicer (and shorter) name would be Maybe<T>
.
cc @bartdesmet and also @MadsTorgersen and @mattwar for their perspective from the original LINQ design philosophy (there have been a few recent discussions about new LINQ operators that could use your insights, I will try to tag you more often).
Let me express an opinion here, as someone who was around for at least part of the original LINQ design. @mattwar was more deeply involved in shaping IQueryable
and may have additional and/or conflicting perspective. :-)
There are probably 10x (unscientific guesstimate) more people using LINQ over IEnumerable
than IQueryable
. We should land on the API shape that is most convenient/elegant/idiomatic for that larger crowd, and solve any IQueryable
-specific problems within the scope of IQueryable
. Degrading the usage experience for everyone isn't the right outcome.
For that reason, TrySingle
etc. should follow the established Try
pattern, with an out parameter and a bool
return value. All the good arguments to that effect above apply.
What to do about IQueryable
? The problem there is that the current IQueryable
machinery isn't able to deliver an expression tree of such a call where part of the result comes through an out parameter. Hence proposals in dotnet/corefx#14276 to instead return a tuple or other compound result.
I think IQueryable
should have such tuple-returning overloads of TrySingle
etc. Those do indeed construct expression trees of calling themselves, like all other Queryable
methods do.
``` c#
public static (bool success, T value) TrySingle
_In addition_ there should be an overload that shadows the out-based one on `IEnumerable`, and simply forwards to the tuple-based one:
``` c#
public static bool TrySingle<T>(this IQueryable<T> source, out T value)
{
bool success;
(success, value) = TrySingle(source);
return success;
}
This limits the damage only to the Queryable
arena, rather than inflict it on Enumerable
users.
The "damage" is:
Queryable
has _two_ public TrySingle
methods instead of one, which may confuse users and query providers alike. The providers will still mostly see the tuple-based one, but may still see the out-based one when nested in other queries. They'll just have to be smart enough to recognize both. For the users, we could hide the tuple-based (less convenient) methods from IntelliSense.Queryable
methods - the out-based overloads- that do not _completely_ follow the otherwise ubiquitous pattern of encoding themselves into an expression tree for providers to see. But it's close enough that I don't think we're deviating morally from that principle: they are encoding _another, equivalent, overload of themselves_.Thoughts?
@MadsTorgersen Is the out variable version of TrySingle known to not work with IQueryable?
@mattwar I'm relying on @bartdesmet's argument to that effect over in dotnet/corefx#14276.
Thanks @MadsTorgersen. From the perspective of optimizing for the most common usage and the hypothesis that IEnumerable<T>
is the most common scenario, I am fine with us doing what you are proposing (which is I believe what people in the thread had already arrived to before I started chiming in :grin:).
I still believe these operators are weird in that they have even more limited composability than the existing operators that return a single element (the argument I tried to make at https://github.com/dotnet/corefx/issues/6073#issuecomment-268405835), but I can be convinced that this is not as important.
@MadsTorgersen :shipit: Sounds like a great idea to me. I think it makes sense to stick with existing conventions (using out
) and not fall too far from the tree. Tuples do have limited readability compared to Try
in this kind of scenario.
With the addition of _out var_ it makes TryXXX pattern API's _less_ unappealing for general code (basically because you don't have to define the variable up front.) Yet, this is still an unappealing pattern for a LINQ type API that is not only primarily expression based, but is sequence based and is likely to have nested expressions that are executed for each item in that sequence that may themselves be needing access to this API. If you only have a TryXXX pattern then this becomes a bad choice if the only way to use it requires you to use the same variable, associated with at best the outer statement scope, for each instance of the iteration. Your query now becomes side-effecting and cannot be processed in parallel.
An API that returns Option<T>, some tuple pattern, or other like structure is better for this sequence style usage, except the language does not yet offer good means to consume these compound values in an expression context.
We did consider this during original LINQ design. This is why we ended up with SingleOrDefault style API. While not perfect, it doesn't cause side-effects while nested and doesn't require new language patterns to consume.
@mattwar Are you talking about something like the following?
c#
from x in collection
where x.AnotherCollection.TrySingle(out var y)
select y
There is a proposal to make that work: https://github.com/dotnet/roslyn/issues/15619.
@svick that would work, but only for query expression syntax. Its an awkward translation if you had to do it by hand. Assuming the translation would work at all.
The PR dotnet/corefx#14276 seems to be abandoned. Unassigning this issue - it is "up for grabs" again, available for anyone to pick it up.
It looks like we got consensus on the design of IQueryable
companion APIs. Can someone please produce the full API shape for IQueryable
? -- Basically list of ALL IQueryable
APIs (incl. which type they will live in - Queryable
?) based on what @MadsTorgersen suggested above:
c#
public static (bool success, T value) TrySingle<T>(this IQueryable<T> source);
public static bool TrySingle<T>(this IQueryable<T> source, out T value);
Once we have it, we can update top-most spec and make it available for someone to implement it.
@karelz One thing to consider, btw, is whether we should use camelcase or pascal case for the tuple's element names, e.g. (bool success, T value)
or (bool Success, T Value)
. I'm not sure if there's an established convention for that yet.
Tuples have argument list semantics rather than structure semantics. I think the more that logic about tuple values is distributive to each individual value, the better. If you wouldn't pascal-case arguments or multiple return values (if we had those), you probably shouldn't pascal-case tuple element names.
Is there anything I can to to help move this forward?
See my last 2 statements above: https://github.com/dotnet/corefx/issues/6073#issuecomment-273845281 and https://github.com/dotnet/corefx/issues/6073#issuecomment-273844248
@karelz Too late for 2.0?
I think so -- see https://github.com/dotnet/corefx/issues/17619#issuecomment-296872132 for details. It is about minimizing risk.
Rushing APIs is never a smart idea. Small mistake can cost you life-time of compat problems. It's just not worth the risk, unless the value is really HUGE. Makes sense?
Yup, I have no problem with that. Just making sure my time is put to good use!
Yep, appreciate it. For best use of time, I'd suggest to check: dotnet/corefx#17619 (top post).
I was happy to read @MadsTorgersen's comment but I have to say, @MadsTorgersen, that I am kind of taken aback by the 'IQueryable
can eat the table scraps' sentiment.
I have to say this is not looking good... there is already a good way to achieve the desired result without muddying up the LINQ API, and the amount of deliberation that has gone back and forth on this issue and its associated PR(s) highlights the shakiness of it.
If the main motivation is the inability to distinguish between a default value in correlation to the count of elements within a sequence, why not use more conventional means? I understand that IEnumerable
is by far the majority use case for LINQ, but I don't think I could see the methods introduced by this proposal being so commonly used as to have enough benefit over some conventional equivalents that accomplish the same end result to warrant introducing so many unprecedented constructs to the LINQ API, like out
parameters, tuple returns, and 'matched-mismatched' operators between Enumerable
and Queryable
:
// Top-level equivalent
var trySingle = xs.Where(x => x.y).Take(2);
var tryFirst = xs.Where(x => x.y).Take(1);
var tryLast = xs.Where(x => x.y).Reverse().Take(1);
var tryElementAt = xs.Where(x => x.y).Skip(index).Take(1);
var anyOneOfThoseExamples = PickOne(trySingle, tryFirst, tryLast, tryElementAt);
var result = anyOneOfThoseExamples.ToArray();
if (result.Length == 1)
{
// Do stuff
}
else
{
// Fall back
}
// Subquery equivalent
var sequence = from x in xs
let y = ys.Where(y => y.x == x).Take(2).ToArray()
select new
{
x,
y = (success: y.Length == 1, value: y[0] )
};
Just my :two: 💰 ...
why not use more conventional means?
LINQ is by definition NOT conventional, since in any case where LINQ is used you can do it using for
/foreach
and collections. LINQ generally (in my opinion at least) provides developers with:
While yes TrySingle
, TryFirst
, TryLast
, and TryElementAt
, can done using existing operators they are less obvious, less performant (have a huge collection and now you have to allocate an array to just find out that it has more than one item?), and do read like a sentence.
Is the LINQ API getting unmanageable? Maybe. But that should be solved by splitting up the methods, not by limiting its usefulness.
LINQ is by definition NOT conventional, since in any case where LINQ is used you can do it using
for/foreach
and collections.
I didn't mean "conventional" as in "let's take it down to the assembly registers", I meant it as in "what LINQ already offers to accomplish this."
less performant (have a huge collection and now you have to allocate an array to just find out that it has more than one item?)
Without doing a thorough analysis, I would assume that somewhere along the line, TrySingle
and kin would need to allocate at least one or two objects, if not a struct under the guise of an IEnumerator
or something like that. Allocating an array is nothing to worry about without profiling; especially in the examples I provided, the array would hold at most one or two elements.
Is the LINQ API getting unmanageable? Maybe. But that should be solved by splitting up the methods, not by limiting its usefulness.
I don't understand what you mean here. I don't think the LINQ API is unmanageable... yet. I think this proposal would be a big step in that direction, though. When you say "splitting up the methods", I think about how easily TrySingle
is achieved by composing Take
and ToArray
. When you say "limiting its usefulness", I think about how TrySingle
doesn't really add any new usefulness -- it adds a sugar over something that was already possible, only this sugar might make Queryable
sick and throw up on itself. 🤢
Without doing a thorough analysis, I would assume that somewhere along the line,
TrySingle
and kin would need to allocate at least one or two objects, if not a struct under the guise of anIEnumerator
or something like that. Allocating an array is nothing to worry about without profiling; especially in the examples I provided, the array would hold at most one or two elements.
I was incorrect there. Yes, in your examples the array would hold one or two items at most. And yes optimization without profiling is never a good idea.
don't understand what you mean here. I don't think the LINQ API is unmanageable... yet. I think this proposal would be a big step in that direction, though. When you say "splitting up the methods", I think about how easily
TrySingle
is achieved by composingTake
andToArray
. When you say "limiting its usefulness", I think about howTrySingle
doesn't really add any new usefulness -- it adds a sugar over something that was already possible, only this sugar might makeQueryable
sick and throw up on itself. 🤢
I think the key word here is "sugar". Yes, TrySingle
and kin are syntactic sugar, but so is all of LINQ, and even plenty of C# and VB.NET. The question (and we're going to have to agree to disagree here) is if the cost of the sugar is worth it. I think it is, and clearly you don't.
I don't know how to judge implementations, but I would have done this rather than foreach
with a flag:
public static bool TrySingle<T>(this IEnumerable<T> source, out T value)
{
if (source == null) throw new ArgumentNullException(nameof(source));
using (var en = source.GetEnumerator())
{
if (en.MoveNext())
{
var temp = en.Current;
if (!en.MoveNext())
{
value = temp;
return true;
}
}
value = default;
return false;
}
}
Just curious because stuff like this is sitting in my Extensions.cs and I may as well use the official implementation if I can deduce from the discussion what that would be.
@jnm2 well Single
is done similarly to yours because it beat foreach
with a flag when compared a while back. Also I think it's more intuitive; look for the thing you want, and then look to see if there's another.
@jnm2 I skimmed through the thread -- looks like we are still waiting on https://github.com/dotnet/corefx/issues/6073#issuecomment-273845281. Nobody picked it up yet:
Can someone please produce the full API shape for
IQueryable
? -- Basically list of ALLIQueryable
APIs (incl. which type they will live in -Queryable
?) based on what @MadsTorgersen suggested above:
c# public static (bool success, T value) TrySingle<T>(this IQueryable<T> source); public static bool TrySingle<T>(this IQueryable<T> source, out T value);
Once we have it, we can update top-most spec and make it available for someone to implement it.
Here's what I care about, plus the IQueryable versions. Are we doing predicate overloads?
namespace System.Linq
{
public static class Enumerable
{
+ public static bool TrySingle<TSource>(this IEnumerable<TSource> source, out TSource element);
}
public static class Queryable
{
+ public static (bool success, T value) TrySingle<T>(this IQueryable<T> source);
+ public static bool TrySingle<T>(this IQueryable<T> source, out T value);
}
}
@jnm2 we need also the other APIs - someone should reread the thread discussion and find out if we planned to do all.
I reread the whole thread twice just now and don't see much mention of either the TryFirst, TryLast and TryElementAt variants or the predicate variants. Certainly not in conjunction with Queryable
. I'm not sure why it's me doing this, but here's everything, just to get the ball rolling:
namespace System.Linq
{
public static class Enumerable
{
public static TSource Single<TSource>(this IEnumerable<TSource> source);
public static TSource Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source);
public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
+ public static bool TrySingle<TSource>(this IEnumerable<TSource> source, out TSource element);
+ public static bool TrySingle<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out TSource element);
public static TSource First<TSource>(this IEnumerable<TSource> source);
public static TSource First<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source);
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
+ public static bool TryFirst<TSource>(this IEnumerable<TSource> source, out TSource element);
+ public static bool TryFirst<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out TSource element);
public static TSource Last<TSource>(this IEnumerable<TSource> source);
public static TSource Last<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source);
public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
+ public static bool TryLast<TSource>(this IEnumerable<TSource> source, out TSource element);
+ public static bool TryLast<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out TSource element);
public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index);
public static TSource ElementAtOrDefault<TSource>(this IEnumerable<TSource> source, int index);
+ public static bool TryElementAt<TSource>(this IEnumerable<TSource> source, int index, out TSource element);
}
public static class Queryable
{
public static TSource Single<TSource>(this IQueryable<TSource> source);
public static TSource Single<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static TSource SingleOrDefault<TSource>(this IQueryable<TSource> source);
public static TSource SingleOrDefault<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
+ public static (bool success, T value) TrySingle<T>(this IQueryable<T> source);
+ public static (bool success, T value) TrySingle<T>(this IQueryable<T> source, Expression<Func<TSource, bool>> predicate);
+ public static bool TrySingle<TSource>(this IQueryable<TSource> source, out TSource element);
+ public static bool TrySingle<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, out TSource element);
public static TSource First<TSource>(this IQueryable<TSource> source);
public static TSource First<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static TSource FirstOrDefault<TSource>(this IQueryable<TSource> source);
public static TSource FirstOrDefault<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
+ public static (bool success, T value) TryFirst<T>(this IQueryable<T> source);
+ public static (bool success, T value) TryFirst<T>(this IQueryable<T> source, Expression<Func<TSource, bool>> predicate);
+ public static bool TryFirst<TSource>(this IQueryable<TSource> source, out TSource element);
+ public static bool TryFirst<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, out TSource element);
public static TSource Last<TSource>(this IQueryable<TSource> source);
public static TSource Last<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static TSource LastOrDefault<TSource>(this IQueryable<TSource> source);
public static TSource LastOrDefault<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
+ public static (bool success, T value) TryLast<T>(this IQueryable<T> source);
+ public static (bool success, T value) TryLast<T>(this IQueryable<T> source, Expression<Func<TSource, bool>> predicate);
+ public static bool TryLast<TSource>(this IQueryable<TSource> source, out TSource element);
+ public static bool TryLast<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, out TSource element);
public static TSource ElementAt<TSource>(this IQueryable<TSource> source, int index);
public static TSource ElementAtOrDefault<TSource>(this IQueryable<TSource> source, int index);
+ public static (bool success, T value) TryElementAt<T>(this IQueryable<T> source, int index);
+ public static bool TryElementAt<TSource>(this IQueryable<TSource> source, int index, out TSource element);
}
}
@jnm2 I'm not sure why it's me doing this
You were interested in the issue and no one else was interested to follow up in the last year, which means it won't happen otherwise. I am just trying to help here to push it forward and unblock contributors (you) :) ... maybe I misunderstood your intent?
LGTM, adding it to the top post (it is considered approved - I will ping API reviewers as FYI).
Thanks @jnm2!
FYI: @OmarTawfik @VSadov
Next steps: it is available for implementation if someone wants to grab it (not blocked anymore).
I'm interested but I must be the least knowledgeable person in this thread... I guess I won't let that stop me in the future. 😂
I'm also interested enough to implement it if no one else does...
@jnm2 Happy to help anyway I can 😄
@SamuelEnglard Can I sign you up to do the implementation? =D
I know the relevant code well, and am happy to do it, but if someone wants to take it as an intro to the relevant libraries and/or to adding to the API I think this would be quite a nice one for them to do.
I only have code for the original TrySingle
currently and free time is something I'm low on currently
Whatever time I give here I won't have for NUnit, but I don't want this issue to get put off any longer. @JonHanna?
NUnit will live :P
Grand, I'll take it so.
Turns out not everyone is happy with the outcome, we will have to do proper API review once again.
Questions:
(bool success, T value)
?Enumerable
?I wondered. That was faster than I expected. 😄
Is there room to postpone some of this complexity and just get the estimated 90% use case via the lone TrySingle<T>(this IEnumerable<T>, out T)
?
I've been on phone for the last part of the thread, and missed that we had something on Queryable that wasn't on Enumerable. Really, bar ToXxx
methods on Enumerable that produce a collection, we shouldn't add anything to one without parity on the other without a very good reason.
On the ValueTuple matter, it seems sensible to me to consider that a separate, and deeper, API design question than TrySingle. Let's pare this down to the bool-and-out pattern and discuss the bool-in-tuple pattern across APIs elsewhere, maybe?
out
doesn't work well in Linq as it forces a mutable variable in the containing scope. Optional<T>
. Mostly because tuples are harder to document and enforce consistency in naming.Optional<T>
would look like (#18159). Concerns:You don't need to have all methods on Enumerable exist on Queryable. ToXXX collection methods will work automatically because IQueryable derives from IEnumerable. You only need methods on Queryable that need to either be implemented differently or converted to an Expression (to be executed later by the provider).
@mattwar that's what I said. Those under discussion here would be in the category where we should have parity.
@terrajobst The excellent LINQ out var proposal (https://github.com/dotnet/csharplang/issues/159), which we already want for other APIs, would make TrySingle
automatically appealing in LINQ.
FYI: The API review discussion was recorded - see https://youtu.be/ZrT0uOsqQlI?t=3282 (25 min duration)
@karelz would here be the right place to reply to the review?
@SamuelEnglard sure, you can continue the discussion here. Given that there are plenty of opinions on this matter, I can't promise everything will be answered here.
1) @terrajobst is right that I based the TrySingle
on the TryParse
pattern. The existing Single
and SingleOrDefault
methods throw InvalidOperationException
. The goal of my TrySingle
method was to avoid that exception.
2) While the TryXXX methods do break the chaining that you can do with LINQ, having the InvalidOperationException
thrown breaks the chain even worse!
I think a possible solution to this might be that the TryXXX methods belong somewhere else. Not part of LINQ but maybe something else?
Also @karelz, maybe remove the up-for-grabs tag?
How about letting the caller decide the shape of the result of TrySingle
?
```c#
public static TResult TrySingle
Func
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));
using (var en = source.GetEnumerator())
{
if (en.MoveNext())
{
var temp = en.Current;
if (!en.MoveNext())
return resultSelector(true, temp);
}
return resultSelector(false, default(T));
}
}
```
Benefits:
var (hasValue, value) = xs.TrySingle(ValueTuple.Create);
xs.TrySingle((found, x) => found ? "Found: " + x : "?")
TryXXX
methods.If we're going that direction, shouldn't it be Func<T, TResult> someSelector, Func<TResult> noneSelector
?
At that point, why not introduce a Maybe struct with a .Map(Func<T, TResult> someSelector, Func<TResult> noneSelector)
method?
@jnm2 Absolutely, you can skin this cat that way too. I used a single result-projection function to demonstrate that tuple construction then deconstruction would just work without much ceremony. Either way, I'm hoping that proposing the approach to let the caller shape the result could help move things a bit.
PS While I find someSelector
and noneSelector
clearer, a single projection function version actually ends up being more flexible because you can create simple, generic & reusable helpers for common cases that can be used wherever such a function would be needed (i.e. all TryXXX
methods without needing an overload for each). Unfortunately, C#'s type inference isn't strong enough to make it usable in a few cases.
If C# gets custom named patterns, one of the first things I'd do is implement three custom patterns named Empty
, Single
, and Many
:
```c#
list switch
{
Empty: /* ... /,
Single(var value): / ... /,
Many(var values): / ... */
}
The `Single` custom pattern, in particular, would automatically work like this:
```c#
if (list is Single(var value))
{
// Use value
}
Collection pattern matching such as []
, [var value]
, and [...var values]
might also be available:
c#
if (list is [var value])
{
// Use value
}
This might happen, and if so it'll be C# 9 or 10. However, it shows that the strikingly similar proposed if (list.TrySingle(out var value))
is a natural syntactic fit both with where C# has been and what it is headed towards.
I would be grateful if this could be considered in time for .NET Core 3.0.
@cston @333fred @jaredpar can you please comment? This one had lots of opinions (see https://github.com/dotnet/corefx/issues/6073#issuecomment-352860874), so it may be tricky to finalize ...
I'm tremendously excited to see https://github.com/dotnet/csharplang/issues/874#issuecomment-489697285:
Tagging @agocke who is writing a proposal for a new kind of pattern that would address this. If I recall, he was thinking something like
collection is [head : tail]
orcollection is [first, second, third]
orcollection is []
(empty).
This looks exactly like my final example in https://github.com/dotnet/corefx/issues/6073#issuecomment-422136663 and I can't think of anything I'd like better. Once foo is [var bar]
is added to C#, I would never use foo.TrySingle(out var bar)
anyway.
Do we have an issue tracking proposal for an Option<T>
type?
We technically have one already: Nullable<T>
. I think it would be very interesting to explore what would happen if we removed the where T : struct
restriction.
I think it would be very interesting to explore what would happen if we removed the where T : struct restriction.
Would that be possible without breaking changes? I'd expect there might be ambiguity between the values new Nullable<string?>(null)
and new Nullable<string?>()
, which shouldn't happen in a true option type. Not to mention that the type string?
might now be confused with Nullable<string>
.
I'd expect there might be ambiguity between the values new Nullable
(null) and new Nullable (), which shouldn't happen in a true option type
This is a reasonable objection -- you may want to represent a nullable, nullable type. That would probably require a new type, and Nullable<T>
should probably be annotated where T : notnull
. But still, I think it would be interesting to explore the consequences.
But still, I think it would be interesting to explore the consequences.
I've wondered about this before as well. One thing I think you'd want with such a scenario is for the runtime to be smart about emitting the generic specialization for a Nullable<T>
when T
is a reference type, and niche fill it. There's no need for a separate bool
field to track whether the internal state is nullable if it's annotated as where T : notnull
, you could just use the internal state of the field itself to make the determination.
I believe niche filling is a general feature in some languages (Rust) but an even simpler version is: what if the runtime special cased Nullable<class>
to have the same shape as a simple reference type instance, and provided identity conversions between the two types? Same idea, just only for Nullable<T>
specifically.
Unfortunately this is what I wanted the T? when T : notnull
syntax for, but it looks like that's being taken by "maybe default" semantics.
Most helpful comment
I am only now catching up with this discussion and I would still like to provide my input:
I can second many of @bartdesmet's comments here, and I agree with the reasoning that if we are going to have these methods on
IQueryable
the version that returns aTuple
(orValueTuple
) seems to be more appropriate that one that uses and output parameter.But in fact I would also argue that methods that return a
Tuple
(orValueTuple
) are a better fit for LINQ's fluent compositional style in general, even onIEnumerable<T>
.The
bool TryGetX(..., out x)
pattern is and has always been a little awkward to use and kind of the opposite of the style used in LINQ. AFAIR there have been multiple attempts to extend the language to make the former easier/more natural and support for tuple syntax is one of those features.It might help the whole discussion if we could arrange examples of how the "success" value returned by these operaros can be used when the operators appear nested in a query in both standard method and query comprehension syntax.