-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Wrapped primitives #2953
Comments
I have been paying attention to recent conversations about encapsulation, embedding, and inheritance, and on the surface this looks like a sane compromise, whereby the whole fragile base class issue is avoided, and we don't introduce visibility modifiers for fields. It's all or nothing (as far as types having fields), just like things are now in the absence of this construct. Something that bothers me about it however is that sometimes the interface of a type in a language like zig includes its size. If the size of u64 changed to something else, it could severely break a lot of code. We expect it to always have the same size. @OpaqueType does not introduce this problem when interfacing with c and hiding type information because its always refered to by pointer, and never by value. Maybe this isn't really a problem at all, and relying on the size of a type that doesn't explicitly communicate it is a code smell. It just seemed to me that this might make the abstraction leaky in a very subtle way. |
I really like your proposal, especially the point where i can add methods to my wrapped types. A real world example would be an OpenGL API: const gl = @cImport({
@cInclude("GL/gl.h");
});
const Error = error { … };
pub fn getError() Error!void {
const e = gl.glGetError();
if(e != gl.GL_SUCCESS)
return …;
}
const Shader = zigbox(c_uint) { … };
const Program = zigbox(c_uint) {
pub fn attach(pgm : Program, sh : Shader) !void {
gl.glAttachShader(@unbox(pgm), @unbox(sh));
return getError();
}
pub fn detach(pgm : Program, sh : Shader) !void {
gl.glDetachShader(@unbox(pgm), @unbox(sh));
return getError();
}
}; Also i would not explicitly name the field values and use builtin functions for boxing/unboxing (because it feels more "special" than just access a fiel named const Second = zigbox( i32){
const getMinutes = fn(self: Second) Minute{
return @box(Minute, @unbox(self.value) / 60);
}
} |
I still think my proposal for non-exhaustive enums is best for a usecase like your GL example. |
Yeah, true. Maybe conversion operators ( |
This proposal was closed in favor of distinct types, #1595, but the use case here could be covered with typedef (proposal #5132 , which in turn is a generalization of distinct types ). const gl = @cImport({
@cInclude("GL/gl.h");
});
const Error = error { … };
pub fn getError() Error!void {
const e = gl.glGetError();
if(e != gl.GL_SUCCESS)
return …;
}
const Shader = typedef(c_uint,.Alias) { … };
const Program = typedef(c_uint, .Alias) {
pub fn attach(pgm : Program, sh : Shader) !void {
gl.glAttachShader(pgm, sh);
return getError();
}
pub fn detach(pgm : Program, sh : Shader) !void {
gl.glDetachShader(pgm, sh);
return getError();
}
};
test "gl/typedef" {
const shadertmp : c_uint = ... // init
const programtmp : c_uint = ... // init
const p : Program = programtmp; // coerce to typedef
_ = p.attach(s);
}
If the typedef config const Program = typedef(c_uint, .Distinct);
test "gl/typedef/distinct" {
const pgm_tmp : c_uint = // init
const pgm = @as(Program, pgm_tmp);
glSomething(pgm) // expects c_uint, but 'Program' coerces down one level to c_uint automatically
customGlFunctionInZig(pgm_tmp); // error. c_uint does not coerce up to 'Program' if
// that was the parameter type specified in 'customGlFunctionInZig'
} |
This idea comes from looking at #1595 and the kotlin feature inline class.
Basically a type of struct that contains a single instance value (of a primitive type), that at run-time is treated as the primitive it wraps. Hence, operators work as normal as long as no types are mixed up.
Maybe it could also be possible to wrap Vectors in addition to the "normal" primitives like int and float.
To distinguish it a bit from normal structs, I suggest the name
zigbox
as that's unlikely to be used as an identifier by third parties.Syntax in the example is somewhat based on #2873 , but the main point should come across anyhow.
The text was updated successfully, but these errors were encountered: