In ML and friends monostate is called unit (and gets used a lot because void returns aren't allowed by the languages). Some have empty types too, which can never be occupied. A function returning Empty can't return, for example, though there are other use cases
But here we are talking about C++, where "void" is a pseudotype that is absolutely inhabited, in some conceptual sense. Any function that is declared to return void and which returns is returning a thing that conceptually inhabits void. In this sense, std::monostate indeed captures the same concept as void, but in a much better way, because it's properly a type, not a pseudotype.
Note: Java does the same thing, effectively, with "Void" which is inhabited by exactly one value: null.
I'd accept that it's not the same as the empty type though, given that void* can be occupied and functions marked void can return. Probably someone with more type theory than me can name this properly
Probably “garbage”. C’s void is not a type and does not behave consistently, it’s a keyword associated with arbitrary convenient behaviours for that case.
If following something like CQS the bifurcation can be thought of allowing “pure” functions and excluding code with a temporal / side-effecting component from higher order code.
Not saying bifurcating on void is the best approach to handle that, but in languages where side effects are a thing something is needed to make sure higher order code and side effecting code mix properly.
And this doesn’t come anywhere near to properly making that distinction anyway: a non-void function can have all the side effects, and a void function can have no side-effects.
It also does not “make sure higher order code and side effecting code mix properly”, it just makes a subset of likely side-effecting code not mix with higher order code at all.
void f();
void v = f();
void g(a){return a;}
v = g(v);
Maybe my imagination is failing me, but I can't see how this can do much good without at least polymorphic functions. template<range R, regular X, invocable<range_value<R>, X> F>
requires same_as<invoke_result_t<F, R, X>, X>
auto fold(R&& range, F f, X accumulator) {
for(auto x: range)
accumulator = f(x, accumulator);
return accumulator;
}
I can call that with a function returning a custom unit type: enum class void_t { Void };
fold(my_range, [](auto&& elem, void_t) { return Void; }, Void);
But not with void: fold(my_range, [](auto&& elem, void) { return; }, void{});
which is very annoying and requires fold to special case 'void' via metaprogramming. template<typename T>
T callWithState(auto f) {
auto old = globalState;
globalState = whatever();
T out = f();
globalState = old;
return out;
}
(forgive any syntax errors, my C++ is very rusty...) fn f() {}
let mut v: () = f();
fn g(a: ()) { a }
V = g(v);
> Maybe my imagination is failing me, but I can't see how this can do much good without at least polymorphic functions.Sure, it doesn’t do much useful to C save make void ever so slightly less wonky.
The compiler does not allow you to do that particular operation out of an arbitrary restriction, but that does not make `void` a true void type. It still holds a monotype value!
int bar() {
std::cout << "Bar" << std::endl;
return 0;
}
void baz() {
std::cout << "Baz" << std::endl;
}
template <typename T> T foo(T (*f)()) {
std::cout << "Foo" << std::endl;
return f();
}
template <typename T> T varfoo(T (*f)()) {
std::cout << "Varfoo" << std::endl;
T a = f();
return a;
}
int main()
{
foo(bar); // Valid (returning an int).
foo(baz); // Valid (returning a void).
varfoo(bar); // Valid (assigning an int).
// varfoo(baz); // Invalid (assigning a void (why???)).
}In C++, there's a very compelling case for making void an actual type, because you can't use void as a templated type, which means that templates involving functions that potentially have void return types require unpleasant amounts of template metaprogramming.
Now that C++ standards committees are considering basic usability fixes (e.g. the long overdue ability to do`namespace com::microsoft::directx { }`) there's a vague possibility that somebody might look into actually fixing this some time before 2040.
C++17 and C++20 both took up usability fixes. std::string::ends_with would be a a C++20 example.
Hopefully C++23 will be similarly open to usability fixes.
> a fancy way to say void?
Less fancy and more workable. Had void been a proper type in the first place it would not have been needed (but also… void had the same issue as nothing, it sounds like an uninhabited type more than a unit type).
Despite that, they could have called it Void, even if the standard library normally uses all lowercase.
Void doesn’t exist in ML
—- Haskell
data Void
(* Standard ML *)
datatype void = Void of voidIn category theory terms, I believe void is the initial type (there is exactly one morphism from void to any other type), whereas monostate is the terminal type (there is exactly one morphism from any other type to monotype).