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

Usage of System.Void as a subtype for every single type #1146

Closed
ghost opened this issue Nov 25, 2017 · 18 comments
Closed

Usage of System.Void as a subtype for every single type #1146

ghost opened this issue Nov 25, 2017 · 18 comments

Comments

@ghost
Copy link

ghost commented Nov 25, 2017

Hi. There is a predefined struct System.Void which is treated specially by C# language. The only thing you can currently do with that struct is to apply typeof operator which is possible only via built in alias void. (var x = typeof(System.Void); is not permitted but var y = typeof(void); is completely legal.)

I would like to suggest removal of that restriction so var x = typeof(System.Void) would be valid C# code. Then it would be much clearer that there exists System.Void structure which is special for many reasons. For example you can't make instance of that type which is a property that would be nice to use in some way. Next paragraph summarize the one special property I think that void structure should have to make it useful.

Firstly I would allow System.Void structure in generic arguments. Then you could write something like List<System.Void> empty = new List<System.Void>() which list would stay empty forever because you can't build instance of special struct System.Void! Then there is only one step to make void structure useful. I suggest System.Void structure to be treated as subtype of every single type. That property sounds weird but I think it would be useful. You could for example write List<int> = empty and also List<List<string>> = empty, etc. I miss in C# something like haskell's [] list which is considered to be list convertible to every other list. The only thing that would be necessary is to modify behaviour of only one single type which is treated very specially even now. I've found also other use cases but this one is the clearest.

(I had tried to search before I wrote this text and haven't found anything similar to my proposal. It is possible that I only can't search well so if this one is duplicate I will immediately close the issue)

@MkazemAkhgary
Copy link

you can write

var empty = new List<int>().AsReadOnly();

to create readonly empty list (not sure what will be the use case). you can also create empty enumerable which is useful some times (when you want to pass empty but non null sequence)

var empty = Enumerable.Empty<int>();

@ghost
Copy link
Author

ghost commented Nov 25, 2017

@MkazemAkhgary Then it wouldn't be possible to write List<object> x = empty; because you can"t implicitly convert list of int to list of objects. What I am proposing is that System.Void is subtype of every type. Then after declaring List<void> x = new List<void>() it would be possible to write List<int> a = x and also List<object> b = x.

Use cases? There would be only one value typed as empty list. That value could be assigned to every other list. Something like that is not currently expressible in the language I think.

@ghost ghost closed this as completed Nov 25, 2017
@svick
Copy link
Contributor

svick commented Nov 25, 2017

What you're asking for is called the bottom type and there is already a proposal for that: #538.

But it wouldn't let you use List<void> the way you propose, because List<T> is mutable. Consider:

List<void> empty = new List<void>();
List<object> l = empty;
l.Add(new object());
Console.WriteLine(empty[0]);

This has to fail somehow, and I think the only reasonable solution is to not allow the conversion from List<void> to List<object>. Though similar conversions should work for covariant types (e.g. from IEnumerable<void> to IEnumerable<object>).

@ghost
Copy link
Author

ghost commented Nov 25, 2017

Thank you for your answer. Bottom type is exactly what I am looking for and I really hope that this will make it into language.

It would be nice that bottom type to be System.Void. In fact void currently have some properties which the bottom type should have. I am going to study some theory of what are the real implications of bottom types but the the whole concept sounds useful.

@ghost
Copy link
Author

ghost commented Nov 25, 2017

@svick I have found out that System.Void is ValueType. Permitting of System.Void in generic list would immediatelly allow us conversions from IEnumerable<void> to IEnumerable<object> and IEnumerable<ValueType> wouldn't it?

I thing that it wouldn't be much work to make this just work. The bottom type is almost prepared and it is void. I have nothing against Never type etc. but I thing that it would be very similar to void.

@svick
Copy link
Contributor

svick commented Nov 25, 2017

@drozdekl By current rules? No, it wouldn't. Generic interface covariance only works on reference types.

@ghost
Copy link
Author

ghost commented Nov 25, 2017

In fact I think that void fits into the whole concept of bottom type:

  1. EVERYTHING is object vs. NOTHING is void
  2. EVERYTHING is convertible to object. vs. void is convertible to EVERYTHING.
  3. The fact that you can't build instance of System.Void would be logical consequence of the fact that void is bottom type. Simple there would be 'infinitely' many conditions for potential value of type System.Void. It absolutely fits into theory of unions and intersections of types which is:
  • union of types is intersection of members
  • intersection of types is union of members

Then object would be Union of absolutely all types and void would be intersection of all types. Sadly I can't describe well how much I thing that all of that fits into whole concept. It would also imply that there exists type void? which could have exactly one possible value - null. Wouldn't it be nice for null to be of type void? which is trivially convertible to exactly nullable types ?

@ghost ghost reopened this Nov 25, 2017
@HaloFour
Copy link
Contributor

Bottom/Never means something very different from void and I don't think it makes any sense to conflate the two. Void can never make sense in a generic or variant scenario since there can't be a value or even a placeholder for a value. For a Bottom type there is at least the placeholder as the IL must push/pop something on the evaluation stack.

@ghost ghost closed this as completed Nov 26, 2017
@MkazemAkhgary
Copy link

default for that matter can be assigned to anything (in c# 7.1), although default is just a shorthand for default(T), not a bottom type.

now imagine you have Foo<T> with field T in it. what would be the value of field T if you had Foo<void>? and what would happen if you call a method on it that operates on T? I read what bottom type is but i think that is not applicable in c#. at least not in clean way. I think that there is nothing that c# can not do without supporting bottom type.

@ghost
Copy link
Author

ghost commented Nov 27, 2017

@MkazemAkhgary default(T) should be compile time error just like it is currently compile time error to use default(System.Void). Bottom type should behave just like the current type System.Void but also should be subtype of every existing type. In fact I do think that every other possibility than System.Void would be strange because otherwise the bottom type should be also subtype of System.Void which does not make much sense.

Methods operating on T would behave just like now because bottom type can't have instance thus you can't really call a method on it.

@ghost
Copy link
Author

ghost commented Nov 27, 2017

I am interested in mathematics and especially in Set theory. I also like thinking about types in C# like classes in set theories. System.Void behaves just like an empty set and the same thing wenexpect of a bottom type. Also sets are due to extensionality considered equal iff they contain the same members and that is why I so like thinking about void as bottom type. Also an empty set is fhe only set which is subset of every possible set. That is thing we expect of bottom type.

@VisualMelon
Copy link

VisualMelon commented Nov 27, 2017

@drozdekl from a purely practical point of view, void would be a bad choice because it already means something else, and System.Void is just the framework type that void represents (which C# and the runtime have some special awareness of), and a method with return type void can currently return (it is not meaningful to return an instance of Bottom, as is apparent because (for example) the set of integers has an empty intersection with the set of strings).

I think it is more useful to consider the System.Void type as the set that contains a single unique void value (like unit in Haskell). The Bottom type would indeed be the empty set (which is also a subset of the intersection between any set, type or otherwise), which would be a detail of the type-system and impossible to instantiate and so impossible to return (a method which reports to return type Bottom could never return).

@ghost
Copy link
Author

ghost commented Nov 27, 2017

I have other question. I am asking it here because I think that it is related to special type System.Void. I searched a bit and found these citations which probably are true:

  1. "The type of a null-literal is the null type (§11.2.7)."
  2. "The null literal (§9.4.4.6) evaluates to the null value, which is used to denote a reference not pointing at any object or array, or the absence of a value. The null type has a single value, which is the null value. Hence an expression whose type is the null type can evaluate only to the null value. There is no way to explicitly write the null type and, therefore, no way to use it in a declared type. Moreover, the null type can never be the type inferred for a type parameter (§25.6.4)"

I am asking whether it would be potentially possible to give name to the type of null. Something like "nulltype" which could be used only in a returning value of function. So function nulltype func() could return only null value and nothing else.

Why I do think this may be not a bad idea? Because it would be possible to bring into language non-returnable functions. It would simple be function with return value of the type nulltype which would be somehow marked (attribute?) that it does not return null. There would be thrown an exception if the function marked by that attribute tried to return anything else than null.

edit: It is probable not possible :( Otherwise there would already be functions which can't return null (otherwise would be thrown an exception) which would simple mean that there is guranteed non-nullability in C# which clearly is not possible. So it is not possible to generate condition if (abc == null) throw something; return abc; before every return in function.

@CyrusNajmabadi
Copy link
Member

default(T) should be compile time error

Sorry, do you mean in general? Or for very specific cases. default(T) definitely has to be allowed for generic type parameters (that's why it was actually added to the language in the first place :)).

@CyrusNajmabadi
Copy link
Member

I am interested in mathematics and especially in Set theory.

So do I (this is what i studied for my masters along with type theory :)). However, i don't want to do things in C# just for the sake of mathematical fun. Doing this sort of thing shoudl be done when it provides significant and substantial improvements to the user across a wide gamut of programming scenarios.

It's unclear to me that this proposal accomplishes that. It seems extremely niche, and doesn't really address any sort of core pain point that users tend to commonly run into. Just, it's mathematically nifty, but that's not something that is honestly that beneficial to our users.

@ghost
Copy link
Author

ghost commented Nov 27, 2017

@CyrusNajmabadi It is currently compile time error for System.Void. And it does make sense because there can't exist an instance of type void.

edit: By non returnable functions I meant something like function where is an infinite loop but now I also think that it does not make much sense.

@CyrusNajmabadi
Copy link
Member

So function nulltype func() could return only null value and nothing else.

Why I do think this may be not a bad idea? It would simple be function with return value of the type nulltype which would be somehow marked (attribute?) that it does not return null. There would be thrown an exception if the function marked by that attribute tried to return anything else than null.

Why would i ever need this? How is that any better than just having a 'void' method. If i can only return 'null', how is that any better than not being able to return anything at all?

Because it would be possible to bring into language non-returnable functions.

We have non-returnable functions. that's what a 'void' method is. You can't return any values from it.

@dzmitry-lahoda
Copy link

dzmitry-lahoda commented Mar 1, 2019

@jdegoes shown me the way I want to do programming in C#. Than he shown good(best?) approach to do it in hybrid language like Scala[2]. At least in server environment.

During my life I had fun doing error handling when error was bool, int, enum, ErrorResult, Option, Exception and throw Exception. Sometimes it was same place where I have to handle 3 of these at once. throw exception handling is not well composable with other ways of doing error handling.

In Scala there is IO interface which could be replicated in C# if we have bottom type(Nothing). And seems we will get good variational composition, I guess. Other means of error handling will lead to more code, not sure how beautiful it will be.

using System;
using System.IO;
public struct Unit{}
public interface IO<in TEnvironment, out TError, out TUnit>{}

public class Nothing // : null // inherits all objects including sealed, but cannot be created
{
    private Nothing(){}    
    public static implicit operator IOException(Nothing a) => default;
    public static implicit operator int(Nothing a) => default;
    public static implicit operator string(Nothing a) => default;
    public static implicit operator bool(Nothing a) => default;
    // should be casted from any type
    //public static implicit operator T<T>(Nothing a) => default;
} 

public class C 
{
    public static void Do()
    {
 	IO<object, Exception, Unit> a = null;
        IO<String, Exception, Unit> b = null;
        b = a;
        
        IO<object, IOException, int> c = null;
        IO<object, Nothing, int> d = null;
        c = d; // error CS0266: Cannot implicitly convert type 'IO<object, Nothing, int>' to 'IO<object, System.IO.IOException, int>'. An explicit conversion exists (are you missing a cast?)
    }
}

Next items may help to implement Nothing (so not sure exactly, need to experiment):
#317 (comment)
#146

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

No branches or pull requests

6 participants