Closed andrewrk closed 5 years ago
is there any particular reason that you think that private fields shouldn't be a thing in zig?
I think this is a pretty important issue, and I'm somewhat torn. Here are the gnarliest bullet points for me.
Today, a struct initialization requires all fields to be set explicitly, either to a value or to undefined
. This is fantastic: you can't forget a field, and if someone quietly breaks the API to a library you use, then your existing code will alert you with a compile error.
If some fields are public and some private, how will struct initialization work? It would be a big fat shame to lose the above benefit.
The change would mean there is no such thing as a private field. A pub fn
of a struct communicates its API, but so do fields. So removing private fields means either:
No. 1 violates "communicate intent precisely", and no. 2 is downright infeasible.
I wonder if there is some way to address this disadvantage with a yet-unexplored language feature.
now that we have default field values are there any other reasons pub
should be removed for fields?
I'm actually partial to the idea of keeping everything public in Zig, but also establishing a firm naming convention for "internal" symbols (e.g. prefix with an _
). If a symbol name is formatted as an internal symbol name, you assume that it's not a part of the stable API.
Obviously this does not outright stop someone from accessing private symbols and watching their program break when a dependency updates, but it clearly communicates the intent of the API author, as much as any other solution to this problem.
My experience with languages that formalize access control, is that they often can't express the kinds of "access groupings" I want for the API I'm writing. It's pretty often that you end up with a group of types that all have good reason to know the internal details of each other, but which export an API that shouldn't require any access to internal details from outside that group. Languages have tried "friend" declarations or "file private"/"module private" symbols to capture these kinds of patterns, and I've found those solutions awkward at best. You end up organizing your code around the language's access control system, instead of around the logical separation between components.
Maybe more importantly, I'm not sure how Zig would incorporate a good formalized access control system with its current model of package organization. The compiler has no semantic understanding of a hierarchical relationship between files.
@lucascharlesmyers
I'm actually partial to the idea of keeping everything public in Zig, but also establishing a firm naming convention for "internal" symbols (e.g. prefix with an
_
). If a symbol name is formatted as an internal symbol name, you assume that it's not a part of the stable API.
I have also thought about that, and maybe a better way to provide encapsulation is through interfaces anyway? That is, no encapsulation within a project, but when importing a 3rd party project or library, you only see what the "provided interfaces" of the library expose. In summary, encapsulation would be a package manager responsibility, not a language responsibility. Of course, naming conventions can still be used like you say.
@user00e00
Swift uses this a lot (in addition to public
and private
and fileprivate
, there's an internal
access level that is accessible by everything in the same module). Things get dicey in that language because access control is intertwined with ABI stability and linking, but as long as Zig considers extern fn
the only way to get a stable ABI, I think having "package-private" symbols would be reasonably ergonomic.
I think there are certain times where it's pragmatic to be able to access the internals of an external package you're using (e.g. working around a bug / an API deficiency), but I can see reasonable counter-arguments to that. Namely, if you allow somebody to work around deficiencies in an external library they're using, you might be making it less likely that the problem actually gets fixed upstream. If you don't allow messing with the private symbols of external dependencies, you at the very least force people to fork dependencies they want to alter, and forks are much more likely to turn into pull requests than workarounds at usage sites.
How's this work with @import
returning normal structs?
@bb010g
If I understand what you're asking, I would think it would be sufficient for @import
to "prune" structs according to some chosen rules. e.g. @import
ing a file gives you the full struct, private members and all, but @import
ing a package excludes private members from the returned struct. It might be prudent to then give those usage cases and semantics different names for clarity, e.g. @import
and @importPackage
, but that's a separate discussion.
Also, thinking about it more, I'm strongly in favor of indicating access level with identifier names, i.e. have it baked into the language grammar that names following a particular pattern are to be treated as private/internal identifiers, in place of access-control keywords. Here's my reasoning:
_symbol
, has very little cost when writing code, and can improve readability.Is there a summary of the reasons for the removal? What is the recommended way to mark fields that are subject to change without warnings?
In C it is a pretty common pattern to declare a struct without any fields in the .h file so that the struct is opaque to client code, and it has to use the api. How would you do that in zig?
In C it is a pretty common pattern to declare a struct without any fields in the .h file so that the struct is opaque to client code, and it has to use the api. How would you do that in zig?
https://ziglang.org/documentation/master/#opaque
However your question is off topic for this issue; consider asking in one of the community discussion areas: https://github.com/ziglang/zig/wiki/Community
Right now Zig lets you put
pub
in front of a struct/union field and it does nothing. Originally I was planning on this doing something, but now I'm not sure that private struct fields should be a thing in Zig.