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.