Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

What's really weird to me is not that C++ has a unit type and picked a weird name for it (that's just C++). The weird thing is how many unit types it has:

- std::nullopt_t

- std::nullptr_t

- std::monostate

- std::tuple<>

And I'm sure there's more.



The distinct types are the whole point. You wouldn't want a std::tuple<> to be implicitly convertible to a std::optional<T> (for arbitrary T), and std::nullptr_t exists to be the type of nullptr, which captures the conversion behaviours appropriate for null pointer literals and has nothing to do with the variant use case std::monostate exists to serve.


If there was a std::unit_t and it was implicitly convertible to optional, tuple and pointer, I don't think that would be worse in terms of usability at all (maybe worse in readability for people who haven't heard of a 'unit' type).

As for the std::variant use case, using std::monostate is only a matter of convention there. You could use any of the other unit types just the same.


std::monostate is explicitly provided for use with std::variant. It's in the <variant> header. Sometimes people use it for other things, but that's really an abuse, especially given defining your own type suitable for such cases is typically as simple as `struct mytype{};`.

Using one type to represent empty literals for optional, tuple and pointer types, implicitly convertible to all of them, would make the compiler accept many obviously accidental constructs. In a world where the maintainers of C++ are trying their hardest to make the language safer what conceivable benefit would there be?


Then you're basically back to "anything can convert back and forth with void " -- the point is to avoid* that.


Why wouldn't you want std::tuple<> to be the same as std::monostate, though? In many languages with a proper unit type such as Haskell and Rust, the zero tuple is the unit type.


What about "void"?


void isn’t a unit type (inhabited by a single value), it’s a “bot” type, I.e. no values inhabit it.


void is the unit type. The fact that it is not constructible is a wart of the language, inherited from C. It would be easy to fix and would simplify a significant amount of generic code.

A function returning bottom cannot return, yet void foo() {} can. In fact it can even return the result of calling other void functions:

   void bar() {  }
   void foo(){ return bar();}
In generic code void is usually internally replaced by a proper, regular void_t unit type and converted back to void at boundaries for backward compatibility.

  [[noreturn]] void bar(); 
would be a candidate for a bottom-returning function, except that [[noreturn]] isn't really part of the type system.


> void is the unit type. The fact that it is not constructible is a wart of the language, inherited from C. > A function returning bottom cannot return, yet void foo() {} can.

Or you could say it the other way, that it is the bottom type, and the fact that it can be used as the unit type for returned values is a wart of the language. Furthermore, void* isn't a pointer of the unit type, it's a type for pointers to undefined/unspecified value types.


nothing is gained by making void a proper bottom type. It would only break existing code. OTOH make void a proper unit type would be backward compatible and actually make the language simpler both from a specification point of view and in practical terms.


I didn't mean that it should be made into a bottom type, even though it sort of looks like it on the surface.

I think the original idea was that void meant "unknown", not "empty" or "non-existent": It was all about whether values could be allocated or not (the wart mentioned above). A plain void variable cannot, but a void pointer can be allocated. For functions, they just reused the keyword to mean "no return value" or "no arguments".


A pointer to the bottom type would make even less sense as an interpretation for void *, since such a pointer couldn't possibly point to any initialized value.


Sorry, I wasn't clear enough: I meant that void is not just a mix of the unit and bottom types, it is also in used for unspecified (unknown actually) types.


I ran into this recently writing some C++20 coroutines. The protocol for delivering values from a coroutine that was previously suspended has two flavors: one for values and one for void. My initial draft just implemented the value version and used a struct VoidTODO {} where void should be.

It's too late now. void pointers are used as a pun to mean "type wildcard." If void were a real thing that could have a size and address, that wouldn't work anymore.


Yes!! Been there done that. Two flavors of EVERYTHING: one to deal with functions that return values; and one to deal with void functions. It's awful.


Void means "it is a syntax error to construct a value of this type". This is not a type that exists in category theory or Haskell. (But similar to the "bottom" type.)

Hence, "void*" - a pointer to something, but it would be a syntax error to derefence this pointer.


In that regard void behaves like any incomplete type. In C and C++ you cannot construct objects of incomplete types nor you can assign through pointers of incomplete types. But you can construct pointers to void and other incomplete types.

Differently to other incomplete types void has some special behaviour: you can declare a function as returning void and return with no arguments is also special cased. You can also cast to void.

A void with proper unit semantics would simply be a complete type instead. The only special case would be return with no arguments implicitly returning a void instance, but that would be pure sugar.


That's a good point. Maybe one could argue that rather than the unit type not being constructible, the wart of C is that functions that return "bot" can still "finish executing without returning".

I would almost rather argue that void is indeed the "bot" type, and a function marked with a void return type shouldn't be said to "return void;" rather we should say that it's an overloaded syntax that means the function has no return value at all. Same for "return bar()" there, that's just a false-friend of the syntax for returning a value, just syntactic sugar for "bar(); return;".


void is not a subtype of all types, though.

C and C++ don't have a type spindle, where void would be at the bottom. Only C++ has the concept of subtype, only in the class system, and the C++ class system doesn't have a bottom type; there is no bottom class that is a base for all the others.

void is not a proper type; it's just a hack shoehorned into a convenient spot in the type system.

Which is why the C++ people have to invent this whole zoo of other things.

If void were a type, then, for starters, "return x;" would be syntactically valid in a function returning void. (Only, no possible x would satisfy the type system, so there would have to be a diagnosable rule violation in that regard.)

A function returning void does not return a type. It doesn't return anything; it is a procedure invoked for side effects.

The same situation could be achieved in other ways, like having a procedure keyword instead of void.

The (void) parameter list is another example of void just being a hack. It was introduced in ISO C, and then C++ adopted it for compatibility.

The 2023 draft of ISO C finally made () equivalent to (void), though it will probably take many decades for (void) to disappear.


> C and C++ don't have a type spindle, where void would be at the bottom. Only C++ has the concept of subtype, only in the class system, and the C++ class system doesn't have a bottom type; there is no bottom class that is a base for all the others.

A bottom type is not the base of all other types.

> void is not a proper type; it's just a hack shoehorned into a convenient spot in the type system.

It is a type, but it is not Regular and it is incomplete. 'return x;' is invalid in a void-returning function because it doesn't type check. 'return void()' or 'return (void)0;' or 'return void_returning_function();' are all valid because they type check.

Making void regular has been proposed multiple times [1]. It is a relatively simple extension but nobody that cares has the time to carry it through standardization.

[1] https://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0146r1...


In C, it is not like that. From the 2023 draft:

6.8.7.5 The return statement

Constraints

1 A return statement with an expression shall not appear in a function whose return type is void.

It's a constraint violation. It doesn't matter what the type of the expression is.

It looks as if C++ made a small improvement here.

Yes, the bottom type is at the bottom of the type derivation hierarchy. That's why the word bottom is there; that's what it's at the bottom of. It's also why it can't have any instances. Since every other type is a supertype, then if the bottom type contained some value V, that value would be imposed into every other type! V would be a valid String, Widget, Integer, Stream, Array ... what have you.


To clarify, it could be that the bottom type is not the base of all types, if the language has a split between some types which participate in that sort of thing and others that don't (e.g. class versus basic types or whatever).

But void is not the base of anything in C and C++.

You could argue that void is in some category of types where it is at the bottom; but no other types are in that category.

There is another problem: a bottom type should be the subtype of all types in that category. That includes being its own subtype. There we have a problem: C and C++ void is not a subtype of void in any sense.


That doesn't seem right to me. I can define a function returning "void", and it can terminate. I would expect that a function returning an uninhabited type can never complete.


And “bot” refers to the bottom type: https://en.wikipedia.org/wiki/Bottom_type




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: