-
-
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
Support dict.field syntax for Dict{Symbol, <:Any} #25750
Comments
Question is, would we want this only for |
I think just defining it for all dicts would make sense with |
|
True, and that would be nice syntax as well. |
👎 Seems like a good step to be closer to javascript. Also this will cause a deviation between API of |
I'm also mostly against this; I think it just starts to be too many ways to do the same thing. |
@JeffBezanson> I'm also mostly against this; I think it just starts to be too many ways to do the same thing. Maybe. But on the other hand, it might be very powerful when writing generic code. I see @yuyichao> Seems like a good step to be closer to javascript. True - but in certain uses cases, that may be a good thing. Even a high-speed Julia application may have to deal with nested dicts in a a low-speed corner (configuration data, JSON, ...), and the code will look much nicer without a flood of square brackets. Besides, we already have |
A while ago there was a discussion about using a character like In fact, I worked hard to get the then-current meaning of Now the discussions in #1974 c.s. have finally led to the implementation of Base.getproperty(d::Dict, s::Symbol) = s ∈ fieldnames(Dict) ? getfield(d, s) : getindex(d, s)
d = Dict(:wallet =>1, :keys => 2)
@show d.wallet
@show d.keys with perhaps somewhat unexpected result in the last line. For that reason I would still consider it an option to use syntax This would first require full deprecation of |
@oschulz FYI, you've completely missed the main objection in my comment. And,
A little bit for
Disagree completely. For one, loading unknown configure dict into
No, |
@yuyichao > FYI, you've completely missed the main objection in my comment. No, I didn't - I just didn't have a good response to your argument regarding the API of |
@yuyichao > For one, loading unknown configure dict into Symbol is already a bad thing I won't argue here, but I'd like to learn why. Wouldn't Symbol be a natural choice if terms repeat frequently? Or is it bad to risk "flooding" the Symbols table? |
I just mean that you didn't reply to them at all, together with most of the objections in #25750 (comment)
If by whatever mean, you are confident that the keys are always known to be within a fixed set it's fine but that's not generally a property you can rely on when loading configure file. Allocating a symbol table entry for |
Honestly, this is one of the features of languages like JavaScript and K that people who use those languages really love, and one that I've personally wanted to be able to emulate for a long time. So what if it's two ways to write the same thing? When has that ever been such a huge problem? If we don't add support for this to |
If strings are better as keys for config.json style objects than symbols, because they don't flood the symbol table, then that should be their representation. But still, being able to access an object in code using syntactic sugar like Base.getproperty(d::Dict{<:AbstractString,<:Any}, s::Symbol) = s ∈ fieldnames(Dict) ? getfield(d, s) : getindex(d, string(s)) In order not to confuse people with the |
It's great for the consumer, but expands the API for the implementer, so the wrapper approach may actually be more reliable and easier to code (SymDicts can wrap any AbstractDict with String keys, and since it only has one field and a small API, it's easier to write
As davidavdav, this isn't a fundamental issue, the code just needs to distinguish between the keys (as Strings) and the syntactic getproperty calls (as Symbols). |
Refusing to let people do We could easily define a very generic |
Would this be only for (e.g. I'm trying to think how this would affect If this is only a sugar that affects If some dictionary types have this, and others don't, then a bit more care is required in generic code, which will have to use the explicit |
@davidavdav We already did that in 0.6. We can get away with it since it only affects implementers , but |
@JeffBezanson , do you mean having |
I think it's okay (maybe even a good thing) that overloading I tried grepping registered packages for |
The deprecation process could be having this in 0.7: function getproperty(d::Dict, s::Symbol)
# print deprecation warning
getfield(d, s)
end That warns people to change how they're accessing the real fields; then we change it to do lookup in 1.0 or 1.1 (it might be a good idea to have throw an error in 1.0 and add lookup in 1.1). |
Sorry, my bad, I used the wrong version of Julia to test this...
Using In how far would it confuse the user, that |
2¢: to my eye I think it is very useful when reading code to be able to distinguish between direct Dynamic scripting languages can afford to come down on the side of convenience here. But it doesn't feel right to me for Julia. However, I can see the attraction of using |
|
We have Adding an ASCII character for direct field access won't really solve the code-transparency problem, since most people will continue to use If half of Julia users use |
The main motivation for |
You can always write an I agree that The inconvenience of |
That is one concern, but could that be handled forbidding overloading a real field names? Another concern is readability and reviewability of code that uses an API with exposed fields. struct FooPoint
x::Int
y::Int
end
convert(::Type{AbstractPoint}, p::FooPoint) = ... To me it seems desirable that to have a compact notation for clients to write |
Yes, I think this is under-appreciated (until you're trying to implement or use something with this feature). If there's a second convenient syntax for |
Here is a wrapper that adds > julia d = PropertyDict(Dict("foo" => 1, :bar => 2))
PropertyDict with 2 entries:
:bar => 2
"foo" => 1
julia> d.foo, d.bar, d."foo"
(1, 2, 1)
julia> d."bar"
ERROR: KeyError: key "bar" not found struct PropertyDict{K, V, T <: AbstractDict{K, V}} <: AbstractDict{K, V}
d::T
PropertyDict(d::T) where {T <: AbstractDict} =
new{keytype(d), valtype(d), T}(d)
end
unwrap(d::PropertyDict) = getfield(d, :d)
Base.getproperty(d::PropertyDict, n) = getindex(d, n)
Base.getproperty(d::PropertyDict{AbstractString}, n::Symbol) = getindex(d, String(n))
function Base.getproperty(d::PropertyDict, n::Symbol)
v = get(d, n, Base.secret_table_token)
if v != Base.secret_table_token
return v
end
return getindex(d, String(n))
end
Base.IteratorSize(::Type{PropertyDict{T}}) where T = IteratorSize(T)
Base.IteratorEltype(::Type{PropertyDict{T}}) where T = IteratorEltype(T)
Base.getindex(d::PropertyDict, i) = getindex(unwrap(d), i)
Base.get(d::PropertyDict, k, default) = get(unwrap(d), k, default)
Base.length(d::PropertyDict) = length(unwrap(d))
Base.start(d::PropertyDict) = start(unwrap(d))
Base.done(d::PropertyDict, i) = done(unwrap(d), i)
Base.next(d::PropertyDict, i) = next(unwrap(d), i) I'm using this in |
Somewhat related to this, I wrote a small package to make it easier in Julia to deal with javascript-like objects, so that you could use (I started this as an exercise for me to better understand macros in Julia) |
I still rather wish we’d done this. Accessing the fields of a Dict is something you absolutely shouldn’t be doing, I’m not sure why we needed to preserve convenient syntax for it. |
Maybe in 1.2? :-) Access to internals of Dict wouldn't count as a behavior that has to be preserved in the 1.x line, right? |
I'm afraid it's probably too breaking to be considered. It could be experimented with, however—if it doesn't break any packages when PkgEval is done, it be considered a "minor change" (i.e. technically breaking but unlikely to break real world code). |
I've done a little grepping of registered packages for usage of internal |
For what it's worth, I have native Julia property access to javascript-like objects now in https://github.com/davidavdav/JSObjectLiteral.jl#the-jsobject-struct, the implementation turns out to be quite similar to @samoconnor's PropertyDicts (but less complete so far...) |
So there's hope, then? :-) |
This could potentially be done for a 1.x release, if not, it would be nice in 2.0. |
Maybe in Julia v1.3? |
Sure, a PR would help. |
@CameronBieganek: you can still do this via |
Yeah, I understand that. I'm just providing more evidence that this would be an actual breaking change. |
I think an easy solution to this problem is to just define Base.NamedTuple(d::Dict{Symbol, Any}) = NamedTuple{Tuple(keys(d))}(values(d)) |
How does the core team feel about this - would this be acceptable for 1.6, or should it (if done at all) wait for 2.0? |
I'm not that much of a fan. Did I miss a compelling justification for this feature? Otherwise it seems like we're introducing an inconsistent interface in order to avoid typing 3 characters. |
I guess an interesting use case would be nested dicts (e.g. for config data, etc.) With nests dicts, config.some.property.of.interest would be more readable and typeable than config["some"]["property"]["of"]["interest"] |
For me it's not about avoiding typing, it's about readability. Readability is paramount. I think the example from @oschulz pretty clearly demonstrates the superior readability of |
A good first step here would be to create a package, say, SymbolicDicts.jl, which provided this functionality. I could imagine SymbolicDict being a wrapper around other Dict types, similar to the OrderedDict implementation in OrderedCollections.jl. |
That seems like a pretty small win (a small gain in readability) in a specific circumstance. If all obj = MyObj(7, "hello", rand(8))
d.$obj = nothing and then complaining that it doesn't work. EDIT: seeing @mauro3's "confused" emoji, the point is that |
I do have a package like that: https://github.com/oschulz/PropDicts.jl (for details, see https://oschulz.github.io/PropDicts.jl/stable/api/#PropDicts.PropDict) |
With PropDicts.jl: x = PropDict(:a => PropDict(:b => 7, :c => 5, :d => 2), :e => "foo")
x.a.b == 7 |
FWIW, I'm trying to implement this in a generic way at https://github.com/joshday/PropertyUtils.jl#indexes, with the idea being that you can map d = Dict(:x => 1, :y => 2)
id = indexes(d)
id.x == 1
id.z = 3
d[:z] == 3 |
It would be very convenient to be able to use
d.foo
with ad = Dict(:foo => ...)
.Obviously, this would require defining methods of
Base.getproperty
,Base.setproperty!
andBase.propertynames
forDict{Symbol, <:Any}
in Base, which I guess would count as a breaking change. But now that #25311 is merged, it sure would be fun to have in. :-)The text was updated successfully, but these errors were encountered: