Proposal: never/bottom type #8603
Replies: 110 comments
-
It would be really nice to be able to use never with variance. Then we could use IReadOnlyList as any type (or just class types?). This would, of course require CLR support. |
Beta Was this translation helpful? Give feedback.
-
I don't understand how "never" could work in generic containers? Would |
Beta Was this translation helpful? Give feedback.
-
That's the idea. It would represent an empty list. It would only be possible to use never in covariant/out positions, of course. |
Beta Was this translation helpful? Give feedback.
-
Java manages the same without a "never" type. That only really works in the JVM due to generic type erasure and the lack of generics over primitives. With the CLR |
Beta Was this translation helpful? Give feedback.
-
@MI3Guy could you please give an example? @gafter has mentioned variance in the original issue as well, but I find it hard to wrap my mind around the idea. |
Beta Was this translation helpful? Give feedback.
-
@orthoxerox
You could also add TError and make ResultValue specify never as that type. |
Beta Was this translation helpful? Give feedback.
-
Well, you also have that pesky issue of classes not allowed to be variant, only delegates and interfaces. |
Beta Was this translation helpful? Give feedback.
-
True. I'll make it an interface. |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
I'm clearly missing something over your use of public sealed class ResultError : IResult<object>
{
public string ErrorMessage { get; }
} but - presumably - that isn't what you are trying to achieve in your example. |
Beta Was this translation helpful? Give feedback.
-
In that case,
But as I stated before, the use of |
Beta Was this translation helpful? Give feedback.
-
In your example, you have made public interface IResult<in T> { }
public sealed class ResultValue<T> : IResult<T>
{
// ...
public T Value { get; }
}
public sealed class ResultError : IResult<object>
{
public string ErrorMessage { get; }
}
_ = new List<IResult<string>> {new ResultError()}; But again, I guess I'm just missing the point. |
Beta Was this translation helpful? Give feedback.
-
Switching to contravariant makes |
Beta Was this translation helpful? Give feedback.
-
Oh! "A new type that has no instances and is a subtype of every other type". Got there in the end. Yes, I now get how it would work with covariant types. That's really nice: I could really use that! 😀 I'm not sure that |
Beta Was this translation helpful? Give feedback.
-
Well, |
Beta Was this translation helpful? Give feedback.
-
The Never type doesn't have to be a subtype of every other type. It should just be a type with no elements. Then there will be a generic function |
Beta Was this translation helpful? Give feedback.
-
@gdar91 Implicit conversions are not enough. One of the main use cases for a bottom type is in generics (e.g. |
Beta Was this translation helpful? Give feedback.
-
As we discussed lists, optionals I also want to point out that Something like this interface IChoice<out TLeft, out TRight> {}
record Left<T>(T Value) : IChoice<T, Never>;
record Right<T>(T Value) : IChoice<Never, T>;
IChoice<int, string> x = Left(1);
x = Right("Hello") Without Never what should the Right type be for |
Beta Was this translation helpful? Give feedback.
-
The very big interest of a bottom type used in conjection with covariance is a variation of the If we have a type
The thing is that releasing a resource is by nature a best effort thing, if we can't release it, at some point we have to give up. Without a bottom type, we can't express the required infaillible behaviour, and we can't use the covariance to return |
Beta Was this translation helpful? Give feedback.
-
The gurantee of being infaillible is the fact that the bottom type is impossible to instantiate, it is just a marker, what we call a |
Beta Was this translation helpful? Give feedback.
-
Yes, actually the previous |
Beta Was this translation helpful? Give feedback.
-
I don't know about for other people, but I often defer my exception throwing to other methods (that obviously never return), usually for the sake of ensuring that all exceptions are written in a standard style eg. (this is a simplified version of it, since I don't want to dig up the original code this example is from) private static void ThrowNullBufferException(string buffername)
{
throw new ArgumentNullException(buffername, $"'{buffername}' cannot be null or contain less than one element");
}
// then anywhere I'm validating a buffer
public static void SomeBufferRelatedMethod(object[] buffer)
{
if(buffer == null || buffer.Length < 1)
ThrowNullBufferException(nameof(buffer));
}
// or in some other cases where the object might not be a buffer, but follows similar rules
public static object SomeListRelatedMethod(IList<object> list)
{
if(list == null || list.Count < 1
ThrowNullBufferException(nameof(list));
} or in my current situation (why I even found this page), I have a number of methods that are guaranteed to throw (like in the previous examples) but they have additional actions that must be performed before the actual throwing can occur (logging the exceptions to a logfile). I'm not sure however that I like either 'bottom' or 'never' as the keyword, perhaps it might be clearer if the syntax was
|
Beta Was this translation helpful? Give feedback.
-
@LupusInferni315 |
Beta Was this translation helpful? Give feedback.
-
For me the big use-case for me for a But I am not a compiler programmer so I am likely wrong. |
Beta Was this translation helpful? Give feedback.
-
That's true, but in the example I gave this would specifically apply to methods that always throw, not situations that enter an endless loop (since the 'endlessness' of a loop is hard to 'determine'), however; I had not considered methods that call |
Beta Was this translation helpful? Give feedback.
-
@LupusInferni315 |
Beta Was this translation helpful? Give feedback.
-
For me (who regularly uses functional languages) the real value of a This is particularly useful for things like With a proper option type, this should be possible:
|
Beta Was this translation helpful? Give feedback.
-
@Richiban |
Beta Was this translation helpful? Give feedback.
-
You're right, I shouldn't have used a value type in my example. I'll remove it for future reference |
Beta Was this translation helpful? Give feedback.
-
See #8604 for the champion |
Beta Was this translation helpful? Give feedback.
-
Previously discussed in dotnet/roslyn#1226.
A new type that has no instances and is a subtype of every other type can be introduced, called
never
orbottom
. It will be the official return type of thethrow
expression and will allow transformingreturn
,break
,continue
andgoto
into expressions (see #176). This will allow them in all expression contexts wherenever
is combined with another type (&&
,||
,??
,?:
) or discarded (expression statements). Instantiating variables/fields of typenever
should be impossible.It can either be implemented as a real CLR type (well, as real as
void
) or as a compiler trick (never
-returning methods are actuallyvoid
with aNeverReturnsAttribute
).Beta Was this translation helpful? Give feedback.
All reactions