Skip to content
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

Inconsistency between fieldnames and propertynames #34788

Open
benninkrs opened this issue Feb 17, 2020 · 3 comments
Open

Inconsistency between fieldnames and propertynames #34788

benninkrs opened this issue Feb 17, 2020 · 3 comments

Comments

@benninkrs
Copy link

Currently, fieldnames returns the fields ascribed to a type, whereas propertynames returns the properties of an instance:

              _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.2.0 (2019-08-20)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> struct A
       x
       end

julia> a = A(1.0)
A(1.0)

julia> fieldnames(a)
ERROR: MethodError: no method matching fieldnames(::A)

julia> propertynames(a)
(:x,)

julia> fieldnames(A)
(:x,)

julia> propertynames(A)
(:name, :super, :parameters, :types, :names, :instance, :layout, :size, :ninitialized, :uid, :abstract, :mutable, :hasfreetypevars, :isconcretetype, :isdispatchtuple, :isbitstype, :zeroinit, :isinlinealloc, Symbol("llvm::StructType"), Symbol("llvm::DIType"))

Since properties are abstractions of fields, I feel that fields and properties should be treated more consistently. I have two independent suggestions:

  1. Currently, the fallback for propertynames is propertynames(x) = fieldnames(typeof(x)). My suggestion would be to define the corresponding fallback fieldnames(x) = fieldnames(typeof(x)) as well. This seems to me to be the only sensible meaning when x is not a type. Furthermore, this addition would be non-breaking.

  2. IMHO, the result of propertynames(A) in the example above is unexpected. I suggest defining the fallback propertyname(x::DataType) = fieldnames(x) (and related methods for special types such as UnionAll and Tuple). I believe this would technically be a breaking change, but it would make the behavior of propertynames(A) consistent with fieldnames(A) and (arguably) more expected.

@JeffBezanson
Copy link
Sponsor Member

I believe these are different for a reason. fieldnames is a property of a type, since all instances have the same field names in that sense. But the way getproperty works allows different instances to have different properties, so propertynames operates on instances.

In 1.0 we tried to eliminate many of the f(x) = f(typeof(x)) reflection methods since they can be quite confusing --- you don't know if you're going to get a property of x itself or of its instances. Such a definition makes f itself less consistent, not more.

@benninkrs
Copy link
Author

@JeffBezanson Your points are well taken. But somehow I still feel that the current behavior is unpleasantly asymmetric and initially confusing. Under the premise that a property is a generalization of a field, then a function named propertynames ought to generalize the behavior of a function named fieldnames. Perhaps changing the names of these functions to look less symmetric would help.

@JeffBezanson
Copy link
Sponsor Member

I do think it would help to more clearly separate functions that operate at the type level, e.g. accept a type as a representative of its instances. For example nfields(Complex{Int}) gives 21 (as now), and TypeLevel.nfields(Complex{Int}) gives 2. I'm not sure what the best way to do that is; it could be a prefix or namespace or something else?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants