-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Julep: Internal fields and functions #30975
Comments
[edit: See Proposal 2 below, which I think is much better] Proposal 1A possible way forward is to change the lowering of getproperty_(x,s) = getfield(x,s) This makes the underscore prefix into a convention for internal fields which can still be flexibly overridden in unusual cases. I think the goals listed above are met, with the exception of 5 which is a bit of a mixed bag. Many packages use underscore prefixed function names for internal functions. Some packages (for example, ZipFile) also apply the underscore prefix to field names though there is no ecosystem wide consistency about this. Then again that's not to be expected as the very point of this Julep is to discuss possible conventions. CompatibilityThe definition above would technically be a breaking change with respect to existing code which overrides getproperty_(x,s) = getproperty(x,s) and document that |
I edited this a bit to emphasize what I see as the problem and goals, rather than my solution in "Proposal 1" which probably fails on aesthetic grounds. I don't think the problem and goals are precisely stated yet and syntax may only be one facet of the larger problem of "why can't I know whether I'm using internal detail of an API by local inspection of the code". |
Here's a counter proposal which attacks the problem from a very different angle. Proposal 2: Module-local lowering of field accessChange the lowering of I think this does much better on several of the stated goals; it kind of "dissolves" goals 1-2, and does far better on goal 5, as it doesn't uglify the implementation details of a module. Implementation sketch: module Mod1
export Private, Public, use_field
# @private struct Private ...
struct Private
a::Int
end
struct Public
a::Int
end
# use_field(x) = x.a
use_field(x) = Mod1.getprop(x, :a) # New lowering for x.a
## The following are auto generated
# Each module gets a default `getprop`
getprop(x, s) = getproperty(x, s)
# Each type gets a method of the module-local `getprop`
getprop(x::Private, s) = getfield(x, s)
getprop(x::Public, s) = getfield(x, s)
# Invocations of `@private struct ...` generate something like
Base.getproperty(x::Private, s::Symbol) = error("`Private.$x` is an internal field")
end
module Mod2
getprop(x, s) = getproperty(x, s) # Auto generated
using ..Mod1
# use_field(x) = x.a
use_field(x) = Mod2.getprop(x, :a)
end
julia> Mod1.use_field(Mod1.Public(1))
1
julia> Mod1.use_field(Mod1.Private(1))
1
julia> Mod2.use_field(Mod1.Public(1))
1
julia> Mod2.use_field(Mod1.Private(1))
ERROR: `Private.Main.Mod1.Private(1)` is an internal field What about cases where implementation details are shared between modules? For example, between several closely cooperating submodules within a project? This could be handled by explicitly importing CompatibilityIntroducing the above is fairly compatible with the existing mostly A transition plan during the 1.x timeframe could be to make this lowering opt-in on a per-module basis, with a compiler meta attached to the module AST. Then switch the lowering completely in 2.0. |
I keep wanting to edit the description... I think perhaps I'll move this to the juleps repo instead. |
(Moved to JuliaLang/Juleps#54)
The problem
Julia APIs use a mixture of public and internal fields so it's never syntactically obvious whether a given field access
x.a
violates API boundaries. This makes it hard to detect these problems when reviewing code and hard to write a linter which can tell the difference. That is, a local reading of the code can't tell the story; you must read the documentation of whatever package you're using.Goals
This Julep aims to make the access of internal fields syntactically obvious. Ideally this is just ugly enough to make you think twice but not so bad that it becomes difficult to use the internal structure of types inside internal functions. Some specific goals:
getfield
.Prior discussion
$
as notation forgetfield
over at Support dict.field syntax for Dict{Symbol, <:Any} #25750.@
also appears to be available and was suggested in the discussion of RFC/Julep: Introduce getproperty onArray
for built-in data tables. #30646; some of the somewhat off topic musings there led to this julep.export
with regard to public symbols has been discussed in many places.The text was updated successfully, but these errors were encountered: