-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Proposal: user-defined null/default check (non-defaultable value types / nullable-like types) #15108
Comments
So it's like an Option type but with error details + syntax sugar? Can't we use a tuple and an extension method? (int result, Exception error) len = s?.Length;
int x = len.GetValueOrDefault();
public static T GetValueOrDefault<T>(this (T Result, Exception Error) source, T defaultValue = default(T))
{
return source.Error == null ? source.Result : defaultValue;
} |
@dsaf This is |
@alrz any decent modern language basically :). So a CoreFX issue unless we want the |
There already exists a proposed (and possibly still planned) feature that would address this requirement: the struct Expected<T>
{
public T Value { get; }
public Exception Exception { get; }
public static bool operator is(Expected<T> e, out T v)
{
v = Exception == null ? Value : default(T);
return Exception == null;
}
} Then you'd be able to do: Expected<int> e;
int x = e is int ? e.Value : 0; Or, using the new " Expected<int> e;
_ = e is int x;
// x will either be default(int) or e.Value here |
I don't like the idea of conflating If the primary goal is to support the |
I smell monad ... It'd nice to see a proposal for defining monad's operators to cover all these cases. It's funny that Rust uses https://github.com/rust-lang/rfcs/blob/master/text/0243-trait-based-exception-handling.md |
@dsaf Yes, Option is also the example. I also wish we can use tuples and extensions. |
Some more examples:
|
To tell the truth, what I want the most is "non-default value types". |
default(T) makes null where T is struct and contains members of reference types. Thus, if C# would have non-null reference analysis, C#should have non-default value analysis too. |
There's nothing special about |
@HaloFour Non-nullable reference types in C# vNext is based on flow analysis. This analysis should contain "defaultability" of value types. |
I see what you're saying. However, in most cases a default value type is perfectly fine and it doesn't make sense to bother warning on them. I think that there would be a lot of potential value in allowing for analyzers to potentially tap into the flow analysis so that custom types like |
@alrz I don't think this should go full monad. For example, consider the To me, the (Plus I'd say it's consistent with LINQ, which technically does go full monad, but is not used that way in practice, and is strongly biased towards the sequence monad.) |
Is the value of disallowing |
Many people want this and I thought it was coming with the #13921 proposal, by allowing parameterless constructors to be defined for structs. Sadly having properly read it, I've realised it's actually a proposal to make things worse. Looks like we'll have to wait a while longer. |
@svick "exactly this purpose" refers to the subject of this proposal. Check out the link in my previous comment. For monads in general, I can imagine something between Haskell's do-notation and F#'s computation expressions, for example, |
@HaloFour @DavidArno |
I disagree (as I explain in a comment near the bottom of that topic). Parameterless constructors on structs will only be useful if |
To recap: This issue might have to be separated into two parts:
Non-defaultable value typesCertain initialization is necessary for Method Contracts, especially Nullability checking. However, if Example 1: Thin Wrapper of ReferenceFrom the viewpoint of performance, we sometimes need to create a thin wrapper struct which contains only few members of reference types, e. g. Given the code such as: struct Wrapper<T> where T : class
{
public T Value { get; }
public Wrapper(T value)
{
Value = value ?? throw new ArgumentNullException(nameof(value));
}
} If Records and nullability checking would be introduced to C#, this could be shortly written as the following: struct Wrapper<T>(T Value) where T : class;
Example 2: Value-Constrained StructsSuppose that you implement a type which value has some constraints: for instance, an integer type which is constrained to be positive: struct PositiveInt
{
public int Value { get; }
public PositiveInt(int value)
{
if (value <= 0) throw new InvalidOperationException();
Value = value;
}
} If Records and Method Contracts would be introduced to C#, this could be shortly written as the following: struct PositiveInt(int Value) requires Value > 0; This looks good at first glance, but, ProposalNullability checking proposed in #5032 is based on flow analysis. Defaultability checking is essentially the same analysis as the nullability checking and could be implemented by the same algorithm. However, in most case, [NonDefault]
struct Wrapper<T>(T Value) whereT : class;
[NonDefault]
struct PositiveInt(int Value) requires Value > 0; I call these types "non-defaultable value types". As with non-nullable reference types, defaultablity should be checked by using flow anlysis. PositiveInt x = default(PositiveInt); // warning
PositiveInt? y = default(PositiveInt); // OK
PositiveInt z = y; // warning
PositiveInt w = y ?? new PositiveInt(1); // OK If Moreover, ordinary value types should not be allowed to have members of non-nullable reference types. Only types that be allowed it would be reference types and non-defaultable value types. nullable-like typesNow, reference types and There are some alternative approaches that use query expressions and task-like, but in my opinion, these are abuses. I would like to use Example 1: UnityEngine.Object
However, int? X(UnityEngine.Object obj)
{
// OK
if (obj == null) return null;
return obj.GetInstanceID();
}
// runtime exception: the native resource is disosed
int? Y(UnityEngine.Object obj) => obj?.GetInstanceID(); So far, this is not so big problem because Unity uses C# 3.0. However, Unity 5.5 will update C# to 6.0. That behavior of Example 2:
|
It would be great to have.
But, It is not so simple as following code.
Business rule about validation should be encapsulated inside type reflecting somehow requires condition. Validation should be offered as a public service of the type. Developers should check input from unsafe resources (keyboard, database, files) before crossing boundary to safe domain type. Data have to be validated before calling constructor and throwing Exception. Look at the great article "From Primitive Obsession to Domain Modelling" by Mark Seemann. Something like.
|
Ported to dotnet/csharplang#146, dotnet/csharplang#147 |
Nullable types -
Nullable<T>
or any reference types - are specially treated with some syntax:?.
operator??
operatorThere are some types behaving like nullable types, and I would like these "nullable-like" types to be "first-class" in terms of special treatment like
?.
,??
, and flow-analysis.Nullable-like types examples
1. value-constrained struct
Suppose that you implement a type which value has some constraints: for instance, an integer type which is constrained to be positive:
If C# compiler would have DbC and record types, this sample would be written like:
This struct is meant not to be zero or less, but can be zero if and only if using
default(PositiveInt)
. Thedefault
should be treated as an invalid value like null.2. Expected
There is a problem with using null as an invalid value, it does not tell why the operation returned null. To solve this problem, some people prefer a type similar to
expected<T>
in C++ - it is a union type ofT
andException
as following:When I use such a type, I want to write as following:
This code uses "exception propagating operator"
?.
and "exception coalescing operator"??
by analogy with null propagating/coalescing operator.Proposed syntax
I want some syntax to introduce "nullable-like" types to C#; One idea is "operator null":
The text was updated successfully, but these errors were encountered: