-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Function overloading - a different approach #2828
Comments
I like that approach as it could be done as pure syntactic sugar. I'm not sure if it could be done fully in user space, but coding a function like const Example = struct {
str: []const u8,
const Self = @This();
fn init(val : var) Self {
if(@typeOf(val) == Self) {
return initWithSelf(val);
}
else {
return initWithString(val);
}
}
fn initWithString(str: []const u8) Self {
return Self{ .str = str };
}
fn initWithSelf(other: Self) Self {
return Self{ .str = other.str };
}
};
pub fn main() void {
var ex1 = Example.init("Hello, world!");
var ex2 = Example.init(ex1);
std.debug.warn("{} {}\n", ex1.str, ex2.str);
} But i think overloading would contradict the Zig zen "Only one obvious way to do things." as it is not obvious what parameter set the function |
That's my biggest gripe with using a comptime approach. On the one hand it's infinitely more flexible, but ultimately buries the variations and all clarity is lost. |
The comptime approach breaks down if the function signatures don't match up exactly. This poses no problem for my proposal. pub const init = overload {
initDefault,
initWithParams,
};
fn initDefault() void {
initWithParams(1337);
}
fn initWithParams(param1: usize) void {} |
Function overloading is one of the things I miss from Java (my second language after PHP...) when writing C and Zig, so I would certainly like to have it in Zig. Although I do like @andersfr 's proposal, I believe that the various declarations for
The downside with this would be that name mangling is required, so an adjustment to the syntax would be required to fix that. Alternatively a new keyword could be used instead of EDIT: Alteration of @andersfr 's proposal to make the function definitions more localised.
Downside here is that the braces imply some kind of scoping where there really is none. |
If we assume #1717 is done, we could also use a pack of anonymous functions instead of named ones: const Example = struct {
str: []const u8,
const Self = this;
pub const init = overload {
fn(str: []const u8) Self {
return Self{ .str = str };
},
fn(other: Self) Self {
return Self{ .str = other.str };
}
};
};
var ex1 = Example.init("Hello, world!");
var ex2 = Example.init(ex1); This would work with named functions as well, but i think this follows more the "one obvious way to do things" than "should i call Problem would be still that we probably could still use As much as i'd like to see overloading, it should fit to the languages concept |
There are multiple reasons I chose named over anonymous functions in the overload set:
The idea that it breaks "one obvious way to do things" by having both init and its overloads available is not true in my opinion. If multiple init functions (or any other similarly named/overloaded functions) exist it is because the API of the struct has a need to support all of these use cases. If the documentation cannot disambiguate their uses it is either bad API design or poor documentation. |
I think this should be more in user-space, by allowing taking
|
You could also use pub const init = fn {
initWithString,
initWithSelf
}; |
Before choosing between Having a Example of homogenous user-space dispatch: const warn = @import("std").debug.warn;
fn dispatch0(arg: usize) void {
warn("dispatch0 called: {}\n", arg);
}
fn dispatch1(arg: usize) void {
warn("dispatch1 called: {}\n", arg);
}
pub fn main() void {
const dispatch = [_](fn(usize) void){ dispatch0, dispatch1 };
dispatch[1](1);
} |
I don't see how |
You are missing that type would-be expanded to express th type of a whole function, much as vectors and arrays also hold subtypes. |
Thinking out loud here, so none of this is at all baked. I like the tooling aspects of the OP's idea. While I do not like that I cannot tell from a given call site exactly what function is returned, it is not too bad as there is only one place to look. A big +1 due to the explicit names. If there was some sort of pattern matching possible, you could almost get away with using a sort of varargs plus switch/case thing (and perhaps this is what was intended with the @typeof discussion):
Excuse all the syntax problems. As I said, this is very much not baked. This would set up multiple signatures for a given function and at compile time, we'd pick the matching one. The function could call out to additional functions as I show here, or you could have the code inline. I would definitely switch on the whole signature, including the return type. Probably too much magic. And I think varargs were dropped at some point. Can't remember. |
This proposal seems like it could be closely emulated with anonymous struct literals, #685: const Example = struct {
str: []const u8,
const Self = this;
// pub const init = overload {
pub const init = .{
initWithString,
initWithSelf,
};
fn initWithString(str: []const u8) Self {
return Self{ .str = str };
}
fn initWithSelf(other: Self) Self {
return Self{ .str = other.str };
}
};
//var ex1 = Example.init("Hello, world!");
//var ex2 = Example.init(ex1);
var ex1 = Example.init._0("Hello, world!");
var ex2 = Example.init._1(ex1); Now it's not overloading of one single identifier anymore, but you do get to group related functions under one name. |
@user00e00 Problem being that you rather defeat the point of overloading if you now have to figure out which of Though being able to use a struct literal there could be an interesting idea for how to do this, perhaps. I think I'd prefer |
Function overloading in the general sense doesn't feel very Zig-like and previous issues have also been closed for this reason.
Here I present a proposal that could work for Zig:
The general idea is that we provide the overload set explicitly and the compiler picks the first one that can be instantiated with the provided parameters. It is quite clear to the user what is happening.
Also, this doesn't require weird name mangling as everything is forwarded to a properly named function that can participate in extern linkage.
The text was updated successfully, but these errors were encountered: