Equal rights for the void!

Hi!

This post may sound strange, but it is about programming languages, so do not care, if you are not interested in programming.

Well, today I want to talk about the type “void” in many programming languages. It is a really special type, because actually it does nothing. In Java you cannot do a lot with this type, I think the only valid usage of “void” is for methods’ result-type, void is not really a type in Java, it is more like the procedure-keyword in Pascal. In Java there are some relicts from C which do not actually fit into the concepts of the language (e.g. switch). However, in C and C++ you can do more with void, it is somehow a “real” type. Such features may appear weird at the first look, but I think they are really nice:

  • You have void-pointers, you can cast any pointer into a void-pointer – explicitly or implicitly
  • You can cast any value into a void-value, which you canot use then – explicitly or implicitly (cf. the definition of Q_UNUSED)
  • You can return any value from a void-function

Well, for long time I could not imagine what this could be good for. But now I think: that is really beautiful. When should implicit casts be allowed? When thinking about it, you may notice two cases: coercion from one type into a more powerful one (e.g. int → long), and coercion from one type into a more general, weaker one (e.g. QWidget* → QObject* or iostream& → istream&). But it is basically the same, consider types as sets or families of possible values/objects, implicit coercion from type A into type B is possible, iff A ⊂ B (A subset of B). And any operation supported for elements of B should be supported by elements of A. Which operations are supported for values of the type void? No operations, so all operations supported for void are supported for all the other types, why should we not cast from any type to void?
That is supported by C++. But unfortunately there are limitations: You cannot have void-variables or constants, but that would nicely fit into the concept and sometimes it would make generic programming easier. You cannot dereference a void-pointer, but why should it not simply be a void-value as usual? And of course it should be possible to allocate void on the heap, it should be just NULL, and the address of a void-value should be NULL, too. It is okay for me, if the compiler wants to warn you about your usage of void, but just for consistency in the language void should not be that different from the other types, and not having variables of that type is simply not very nice. Equal ights for the void!

However, there are two things which may be not so nice: 1. When considering two sets represented by two types and one set should be a superset of the other set, you have to consider some elements of those different sets being equal, and of course in practice you do it all the time when comparing ints with shorts etc., but for voids that is impossible, you cannot decide, if two void-values are equal, and 2. you cannot explicitly cast back from a void-value to a non-void-value. However, void-values are not the only ones, where comparison is impossible, though for the most frequently used types it is of course possible. Consider functions (handled at runtime) internally stored using some kind of an arithmetic formulas, Turing-machines, Lambda-calculus or whatever. Generally it is not possible to decide, if two of such functions are equal (unless the arithmetics are too simplistic), notice that there are many different representations for the same function. So requiring values to be comparable is not very clever in a general-purpose programming language. In practice for some types you may not even want to support the equality-test, because it may be difficult to implement or very slow or something like that. That are my reasons, why I think that the first issue is void. I currently cannot imagine any two types different from void where explicit casting is only possible in one direction. Maybe you can? However, if the type does not support equality-checking, after casting back and forth you cannot even say that the value is identical to the original one. But this issue remains unsolved. However, altogether I think that void should be a real type in our programming languages, because it is very consistent, and it should be different from nil/NULL/None, because nil/NULL/None should indicate the absence of a value of one specific type, and not the void.

Or I can put it that way: The void is like qualia: it is everywhere, it is contained in each individual, it does not express anything specific, you cannot differentiate between “my” and “your”, and of course you cannot cast it back to the individual with its personality.

:)

PS: My statements about allocation a position 0 were wrong in my opinion. Cf. the comments.

12 Responses to “Equal rights for the void!”

  1. Arno Says:

    If you’re interested in a language that does this, you might want to take a look at scala. In scala, ‘Unit’ is what ‘void’ is in other C-like languages – but you can actually declare and ‘use’ variables of type Unit.
    Unfortunately it’s mainly JVM based, but it has support for different kinds of backends and a LLVM backend is in the works.

  2. The User Says:

    I had not yet a detailed look at Scala, but regarding type hierarchies it seems to be very nice (container API). But I do not like it, because it does not support value-semantics, the builtin types will always behave different than your own types, that is ugly (it is of course because of the JVM-limitations). I have read that it has to create bunches of strange classes to implement their object-model with Java, that is ugly, I hope it is better with LLVM.

  3. martyone Says:

    What would be the size of the void variable? One? two? thre? bytes? ;-)

  4. illissius Says:

    Type theory makes a distinction (note that I’m not referring to the C++ type here) between ‘void’ (which has no possible values at all) and ‘unit’ (which has a single possible value — commonly also called ‘unit’). C++’s void type is actually a lot more like ‘unit’. Because there is only one possible value, it is guaranteed to always be the same value, so a unit conveys no useful information whatsoever. So functions in C++ which return C++’s void type are implicitly always returning that one possible value, and conceptually speaking, the caller immediately throws it away (because, again, it contains no useful information). If C++’s void were equivalent to the type-theoretical void, you couldn’t use it as the return value for a function (presumably you could still have a pointer to type-theoretical void, but you couldn’t deference it), because it’s impossible to construct any value of that type whatsoever, and so the function could never return.

    Actually: you *could* use it as the return type for a function, but only for one that is, in fact, guaranteed to never return. Say: one that calls exit(), or enters an infinite loop, or crashes, or throws an exception. In type theory this kind of thing is referred to as a ‘bottom’, a “value” which is implicitly a member of every type. Think about it: if a function is declared to return ‘bool’, it might return true or false, but it might also throw an exception, enter an infinite loop, etc. You can think of these as a third value which it might ‘return’.

    Many functional programming languages reflect this kind of type theory quite closely. Maybe Scala too, I’m not sure.

  5. Kevin Kofler Says:

    GCC/g++ has a noreturn attribute for functions which never return (which obviously should also have return type void since they can never return any useful value).

  6. The User Says:

    @Kevin Kofier
    I think it is the same in LLVM/clang. :)

    @martyone
    It would fit into 0 Bytes. :)

    @illissius
    Do you have an example for such a language?

    Btw @all:
    Have you watched “Enter The Void”? It is like dereferencing a null-pointer. :D

  7. illissius Says:

    Haskell for sure; most likely also the various languages in the ML family, though I’m less familiar with them (SML, OCaml, F#…).

  8. The User Says:

    Hmm, maybe it is a better idea to say “void has one byte”. It would be just “struct void {};”. Or maybe:

    struct Unit
    {
    template<typename T> Unit(const T&) {};
    };

    @illissius

    In Scala, the bottom type is denoted as Nothing. Besides its use for functions that just throw exceptions or otherwise don’t return normally, it’s also used for covariant parameterized types. For example, Scala’s List is a covariant type constructor, so List[Nothing] is a subtype of List[A] for all types A. So Scala’s Nil, the object for marking the end of a list of any type, belongs to the type List[Nothing].

    Nice. :)

  9. martyone Says:

    > It would fit into 0 Bytes. :-)

    Great! and what would be the address taken from such a variable? Where (how?) in the address space would you allocate those 0 bytes to store the value? :-D

    I guess you got my point and now you clearly see there is no way for C/C++.. ;-)

  10. The User Says:

    @martyone
    I suggested allocating it at 0. However, I have already corrected myself: It would be more consistent to give it one byte, and when the address is not actually needed it would get optimised out. There is no way for C/C++ for historical reasons, but in a similar language supporting pointer-arithmetics etc. it would be possible, in my opinion. And try out the Unit-type I have posted above, it will behave like it should do in C++:

    • Everything castable into Unit
    • Usable as a return-value
    • Size: 1 Byte
    • No information
    • Will be optimised out

    What is the problem with it? Well, implicat casts to Unit* are missing, but that is just because of some minor limitations of templates and implicit casts in C++. Add it as a base-class for everything, replace the builtin-types and it would work.

  11. martyone Says:

    > What is the problem with it?
    Maybe this?
    “”"2. you cannot explicitly cast back from a void-value to a non-void-value.”"” :-)

  12. The User Says:

    Ah, sorry, I have to explain it: I was not speaking about any practical implementation, but about “how it would be nicer”. And there is no fundamental property of C++ which would prevent a compiler from implementing a small extension allowing void to be used as a regular type with -fenable-void-as-unit. It would fit into the concepts of C++, though C++ is not very consistent at other places, too.

Leave a Reply

XHTML: Use <blockquote cite="name"> for quotations, <pre lang="text    ∨ cpp-qt ∨ cpp ∨ bash ∨ other language"> for code, [latex] for formulas and <em> for em. Contact me if the comment does not get published, it may have accidentally been marked as spam.

Anti-Spam Quiz: