54

What were the design decisions that argued in favour of void not being constructable and not being allowed as a generic type? After all it is just a special empty struct and would have avoided the total PITA of having distinct Func and Action delegates.

(C++ allows explicit void returns and allows void as a template parameter)

Thomas Owens
  • 79,623
  • 18
  • 192
  • 283
  • For design decisions you would need to ask someone like Eric Lippert. – Oded Jan 20 '12 at 13:46
  • 14
    @Oded Considering that [Eric Lippert has an account and participates on Programmers](http://programmers.stackexchange.com/users/6505/eric-lippert), I think he just did. – Thomas Owens Jan 20 '12 at 13:48
  • @ThomasOwens - I knew he was (very) active on SO, didn't know about Programmers. – Oded Jan 20 '12 at 13:56
  • I'm having a hard time understanding the use case. So, in C++, what is an example of a use case where using void for a template parameter is useful? – Jonathan DeCarlo Jan 20 '12 at 13:58
  • @Oded I've seen him post a few times. He's not very active (less than 100 answers), but he appears to visit somewhat regularly and I would suspect that he might come across a question like this one. – Thomas Owens Jan 20 '12 at 14:01
  • @ThomasOwens - Definitely if his name is mentioned ;) – Oded Jan 20 '12 at 14:03
  • @JonathanDeCarlo - If you have a template method in a class then that method might have return type of void. If you want to generically return the result of any function, then you need to be able to return void. Therefore by not allowing void for a template / generic parameter you have restricted the way the language can be used. C# does not allow a void generic type so you have to treat methods with a return value and methods without differently. Boost uses just one template function object: http://www.boost.org/doc/libs/1_48_0/doc/html/function.html –  Jan 20 '12 at 14:24
  • I'm sure that there were good reasons that this decision was taken and I'm interested to know what they were. –  Jan 20 '12 at 14:25
  • 1
    @ThomasOwens: I thought requiring insider knowledge / only one person could possibly answer was grounds for excluding a question. It would be on SO anyway. – Ben Voigt Jan 20 '12 at 15:32
  • 4
    @BenVoigt It doesn't necessarily require a single individual's knowledge, although there is a single person here who can (easily, I'd assume) give the single right answer. Anyone familiar with the specification can probably answer the question, especially if they have a knowledge of programming language design. It's also an interesting language design/language implementation question, which is on-topic here. – Thomas Owens Jan 20 '12 at 15:35
  • @ThomasOwens: If the question were "Why is it better not to allow use of `System.Void` in generics?", that would be true, but the actual question is "What were the design decisions?" which can only be answered by the designer(s). On SO this would be closed as too localized. – Ben Voigt Jan 20 '12 at 15:39
  • 6
    @BenVoigt: Why are you so keen to have a perfectly valid and interesting question closed for no good reason, other to satisfy your own pedantry? I had a search and couldn't find the answer; maybe someone here has read a blog post about it, maybe the people who took the decision want to answer the question, maybe someone got chatting to the people who made the decision about this and asked the question themselves and can pass on the answer. I've stopped posting on this site and SO pretty much because people seem much more interested in getting a question closed than in answering questions. –  Jan 20 '12 at 15:52
  • 4
    I could point out the the "big point" questions and answers here and especially on SO are generally of the form "what does this syntax mean?" Those questions hardly ever get closed because everyone can pile in and reel out the definitions for const poiners to const objects. The more interesting questions that lie off the beaten track get hounded off the site for some pedantic reason: seriously it's only a few kilobytes in a data warehouse somewhere. Can we all just relax and embrace learning rather than imposing arbitrarily interpreted rules that serve very little purpose? –  Jan 20 '12 at 15:58
  • @TheMouthofaCow: Some things are much better suited to a blog article somewhere than a Q&A site. Let's say someone posts an answer to this question. How would anyone know whether it was true? The whole stackexchange voting system breaks down when you have questions that the programming community in general can't know the answer to. – Ben Voigt Jan 20 '12 at 16:32
  • I may be completely wrong but I seem to remember John Skeet mentioning this in his book. I don't have a copy at hand to verify. – Mike L. Jan 20 '12 at 16:41
  • 1
    @BenVoigt I think Winston Ewert's answer below completely invalidates your point; information about this exists in the public domain, but it is not necessarily that easy to find - this is exactly the type of question that is suited to a Q&A site. If I asked "Why did Neil Armstrong say 'one small step for man...'" would you say that an answer should not be attempted because only Neil Armstrong could answer it? Maybe in the future you can stop whinging and let people who actually enjoy finding out about stuff do so in peace. –  Jan 20 '12 at 17:22
  • If there's something you want brought to my attention, you can always use the contact link on the blog. – Eric Lippert Jan 20 '12 at 19:37
  • 2
    @ThomasOwens: I am in a sense more active on this site than on SO. On this site I post an answer to about one question in every 300. On SO I post an answer to about one question in every 1500. The difference in sheer-number-of-answers activity is attributable to the much higher number of opportunities for answering C# questions on SO. – Eric Lippert Jan 20 '12 at 20:05

2 Answers2

72

The fundamental problem with "void" is that it does not mean the same thing as any other return type. "void" means "if this method returns then it returns no value at all." Not null; null is a value. It returns no value whatsoever.

This really messes up the type system. A type system is essentially a system for making logical deductions about what operations are valid on particular values; a void returning method doesn't return a value, so the question "what operations are valid on this thing?" don't make any sense at all. There's no "thing" for there to be an operation on, valid or invalid.

Moreover, this messes up the runtime something fierce. The .NET runtime is an implementation of the Virtual Execution System, which is specified as a stack machine. That is, a virtual machine where the operations are all characterized in terms of their effect on an evaluation stack. (Of course in practice the machine will be implemented on a machine with both stack and registers, but the virtual execution system assumes just a stack.) The effect of a call to a void method is fundamentally different than the effect of a call to a non-void method; a non-void method always puts something on the stack, which might need to be popped off. A void method never puts something on the stack. And therefore the compiler cannot treat void and non-void methods the same in the case where the method's returned value is ignored; if the method is void then there is no return value so there must be no pop.

For all these reasons, "void" is not a type that can be instantiated; it has no values, that's its whole point. It's not convertible to object, and a void returning method can never, ever be treated polymorphically with a non-void-returning method because doing so corrupts the stack!

Thus, void cannot be used as a type argument, which is a shame, as you note. It would be very convenient.

With the benefit of hindsight, it would have been better for all concerned if instead of nothing whatsoever, a void-returning method automatically returned "Unit", a magical singleton reference type. You would then know that every method call puts something on the stack, you would know that every method call returns something that could be assigned to a variable of object type, and of course Unit could be used as a type argument, so there would be no need to have separate Action and Func delegate types. Sadly, that's not the world we're in.

For some more thoughts in this vein see:

Pang
  • 313
  • 4
  • 7
Eric Lippert
  • 45,799
  • 22
  • 87
  • 126
  • C++ supports it without having to use a magical singleton type. But I assume that has to do with C++ using a completely different "generics" style then C#. – Winston Ewert Jan 20 '12 at 20:37
  • 5
    @WinstonEwert - I'm pretty sure C++ doesn't support generics at all, but rather has templates, which are fundamentally different. As I understand it, with templates the compiler is doing a global search and replace with your type parameters, but with generics, the type system is creating a proper type. I also think that's why C++ template errors were so obtuse. I'm sure Eric will correct me if I said anything not quite right – Adam Rackis Jan 20 '12 at 20:56
  • 1
    @AdamRackis, that's why I had generics in quotation marks. – Winston Ewert Jan 20 '12 at 20:59
  • 1
    @AdamRackis: Couldn't have said it better myself. Templates are in some sense not a *type system* feature in C++. (In another sense they define a whole new type system of their own, but let's not go there.) – Eric Lippert Jan 20 '12 at 21:00
  • There is also a purely semantical reason. What the heck would this code mean, `void x = Action();` ? – Olivier Jacot-Descombes Jan 20 '12 at 23:48
  • @OlivierJacot-Descombes: If "void" were a "Unit" type then it would mean "invoke the action and then assign a reference to the Unit singleton to the variable x". But since "void" means "no value at all", you're right that it makes no sense as a type in any context other than a return type. – Eric Lippert Jan 21 '12 at 00:05
  • I find it rather surprising how close the CLR already is to a Unit-like void. You already have the ineffable (in C#) System.Void type, of which instances cannot be created. If were a reference type, instead of a value type, it would inherently have only one permissible (non-)value: null. That honestly seems fitting, seeing as both void and null represent the concept of "nothing". If the CLR had been designed that way, void functions would actually return null (which would usually be popped immediately, but could be saved to a variable of 'void' or 'object' type, to allow for use in generics). – Kevin Cathcart Mar 27 '12 at 14:17
  • 1
    I think all structs is handled at compile time to get proper placement on stack and in other places where they are inlined. Thus `Void` should be ok to be passed as infinite amount of arguments inside of 0 stack bytes. When any structure needs converting to `object` or call for method (to get `this`) it boxed with placing on the heap with `Type` reference inserted before fields area of memory (for many CLR implementations) and thus could be handled properly. As for me type is just a groupping of objects that can be distinguished by some characteristics (fields). `Void` is just one object. – ony Oct 18 '12 at 10:33
  • 1
    @OlivierJacot-Descombes: If `void` were an empty value type, then that statement would do translate into zero instructions of machine code. Not very useful for hard-coded type of `Void`, but useful in cases where a type has multiple generic parameters but some aren't needed in certain cases. – supercat Mar 18 '14 at 23:52
  • @supercat: Yes, therefore the need for all the `Func<>` as well as `Action<>` overloads. – Olivier Jacot-Descombes Mar 19 '14 at 13:41
  • 2
    @EricLippert: Proper handling of value-type return values would require the ability to pop a variable number of words off the stack. Would there be any fundamental difficulty with allowing the number of words to be zero? If the caller is responsible for allocating space and passing a byref, the type system would have to recognize that a void byref has no meaning, and perhaps that wasn't considered worth the effort, but I don't see any "fundamental" problem. – supercat Mar 19 '14 at 15:28
  • Reading this post now, and Kotlin programming language introduced a return type `Unit` for their methods when it returns void. – Tarik Mar 26 '17 at 12:07
  • I understand that value types and reference type are handled differently by the compiler when dealing with generics. Maybe void can be another special case. There is also a [proposal to allow void type as a generic parameter](https://github.com/dotnet/csharplang/issues/696). – Pablo H Feb 22 '19 at 12:17
  • @WinstonEwert C++ still has warts around `void` like rejecting templates that expand to `void r = func();` etc. – Caleth Mar 08 '21 at 13:04
10

From: http://connect.microsoft.com/VisualStudio/feedback/details/94226/allow-void-to-be-parameter-of-generic-class-if-not-in-c-then-just-in-runtime

This is actually by design - we don't allow any instance of the void type (along with many other types in the runtime). You can't CreateInstance a void or void[] either. We plugged these type holes for security.

I can't for the life of me see how void is a security hole, but... such it says.

Winston Ewert
  • 24,732
  • 12
  • 72
  • 103