Comment by ioanaci
I commented this before but deleted it because I hadn't tested my code properly. Anyway, here is a version that works.
You're right Zig isn't quite as powerful, but you don't need type erasure if you have enough monomorphization. It's a bit annoying to use since you have to capture the comptime version of b inside the inline switch prongs, and get_value does need to take a comptime bool instead of a runtime one. It's functionally the same, except that instead of using the bool to prove the type of the type-erased value is correct and delaying the actual if (b) check to runtime, we're moving it to compile time and instead proving that b has the right value for each case, then generating specialized code.
This does NOT mean that it isn't dependent on user input, the call do_thing(get_user_input()) would absolutely work. do_thing has no compile-time parameters at all.
I don't have the natToString thing because it's a bit more annoying in Zig and would obscure the point.
const std = @import("std");
fn get_value(comptime b: bool) if (b) u32 else []const u8 {
if (b) return 12
else return "string";
}
fn do_thing(b: bool) void {
switch (b) {
inline true => |known_b| std.log.info("{d}", .{get_value(known_b)}),
inline false => |known_b| std.log.info("{s}", .{get_value(known_b)}),
}
}
pub fn main() void {
do_thing(true);
do_thing(false);
}
You could define a function fn pick_type(comptime b: bool) type {
if (b) return u32 else return []const u8;
}
and change the signature fn get_value(comptime b: bool) pick_type(b)
if you wish.Perhaps more convincingly, you can use inline for with a similar effect. It's obviously horrible code nobody would ever write but I think it might illustrate the point.
fn do_thing(b: bool) void {
inline for (0..2) |i| { // go through all bool variants
const known_b = (i != 0);
if (known_b == b) {
// Essentially what we have now is a known_b which is proven
// at compile-time to be equal to whatever runtime b we have.
// So we're free to do any kind of dependent type things we'd like
// with this value.
const value = get_value(known_b);
// Here we know the type of value but it still depends on the runtime b.
if (known_b) std.log.info("{d}", .{value}) // treated as an int
else std.log.info("{s}", .{value}); // treated as a string
// We can also just decide based on the type of value at this point.
if (@TypeOf(value) == u32) ... else ...;
// Or, a switch
switch (@TypeOf(value)) {
u32 => ...,
[]const u8 => ...,
}
}
}
}