Julia: Document preferred naming convention for getters/setters in style guide

Created on 5 Jun 2016  ยท  25Comments  ยท  Source: JuliaLang/julia

I think it would be useful to add a recommendation regarding the naming of getters and setters to the style guide here: http://docs.julialang.org/en/latest/manual/style-guide/#use-naming-conventions-consistent-with-julia-s-base

For getters it looks like the rule is pretty clear: just use the name of the field, e.g. names(x). For setters, I'm not sure we're clear on whether names!(x) or setnames!(x) is preferred. Cf. this thread. Could we agree on a convention? If that's still undecided, probably better make a quick survey to choose the rule most commonly used in packages.

doc

Most helpful comment

Personally I prefer the setXXX! convention, as it describes exactly what the function does. Also it makes it easier to find the correct setter via tab-completion.

e.g. for the linked issue, it was completely unclear to me what ordered! would do: my initial guess was that it was something like sort!.

All 25 comments

In Bio.jl, we use name!(obj, newname) rather than setname!(obj, newname) (https://github.com/BioJulia/Contributing/blob/master/CONTRIBUTING.md). I don't remember why, but it seems to be accepted by members in Bio.jl.

As I recall, using fieldname(x) for the getter and fieldname!(x, newfieldvalue) for the setter were preferred for consistent simplicity once the decision to use change! to mean change-in-place was made.

That's fine with me, that's also the convention used in DataArrays and DataFrames. Though I just noticed NamedArrays follow the set* convention.

@davidavdav Would you be OK with switching to the fieldname! rule?

My concern with this naming convention is that it is very easy to shadow
getters with local variables that have the same name. Any thoughts on how
to avoid that?

Though I agree name conflicts are often annoying, I'm afraid the getters naming convention is too established to be changed at this point.

So then we have fieldnames(object) that shows the member variables in a type, and fieldname(object) to show names of internal structures of the type? Is that not a little bit confusing? (Especially for native speakers of languages that don't have plural forms)

As an unrelated topic, is this a moment to consider lobbying for a syntax like df$field meaning something like df[:field] again? $ for XOR is IMHO a waste of ascii operator space https://github.com/JuliaLang/julia/issues/1974.

So then we have fieldnames(object) that shows the member variables in a type, and fieldname(object) to show names of internal structures of the type? Is that not a little bit confusing? (Especially for native speakers of languages that don't have plural forms)

No, the plural is not the question here. The debate is setX!() vs. X!(), with X the name of the field (which may not even exist in the underlying type). fieldnames was just an example of a name.

As an unrelated topic, is this a moment to consider lobbying for a syntax like df$field meaning something like df[:field] again? $ for XOR is IMHO a waste of ascii operator space #1974.

As you say, that's quite unrelated, so I'd better keep this debate separate. I have no idea when is the best time for this, though.

Oh, all right then. The question is then to change setnames!(::NamedArray ,...) into names!(::NamedArray, ...) . I don't have a problem with that, specifically because we can just define both for a while.

In the particular case of _fieldname_== names, where should we import names from? It is currently a deprecated method from Base, and names! does not exist there. Well, this is another unrelated subject I suppose.

OK, cool. Only some methods for names are deprecated, so it's OK to extend it. names! doesn't exist in Base unfortunately, which means it's doomed to create conflicts between packages until we find a home for it.

yes, it would be nice if there is a general solution for these cases. Similar problems occur with ambiguities, when two packages extend a method from base in different ways.

Personally I prefer the setXXX! convention, as it describes exactly what the function does. Also it makes it easier to find the correct setter via tab-completion.

e.g. for the linked issue, it was completely unclear to me what ordered! would do: my initial guess was that it was something like sort!.

Another argument is that the unofficial convention in Base seems to be if both XXX and XXX! exist, they both do pretty much the same thing except the latter can mutate its arguments, or accept a pre-allocated object e.g. sort/sort!, quantile/quantile!, cholfact/cholfact!. This would not be the case with getter/setters, as they have different fundamental purposes.

I am happy enough with the set...!() version for setters. Indeed it shows a clear difference from the sort() sort!() pairing.

While the earlier patterns of use were as I mentioned

get: ( )
set: !( )
ask: ?( ( ) )

or

get_( )
set_( )
ask_( , )

When paired wirh the getless form of field retrieval, there is some pragmatic utility to the setfull form of field reassignment:
It is visually too easy to gloss over the !()
and misread the in place set as the get.

All considered, use of get___ with set___ for individual fields of a type (and for implicit/computed/composite entities that inform or enhance a type) is more sanity-preserving and less inviting of incorrectness.

If set___ stays, I am using get___ as this keeps coding with Julia nearer the immediacy of expressing design.

Honestly speaking, I think setter and getter interfaces should be achieved by overloading object.attribute. The discussion happens here: https://github.com/JuliaLang/julia/issues/1974. If this really happens before the next Julia release (i.e. Julia 0.6 or Julia 1.0), I prefer it to any options discussed above.

Please keep this issue focused on the naming convention. It's kind of orthogonal whether we recommend using field overloading later: right now there are many setters and getters out in the wild, and we need a convention for them.

Indeed, having a consistent convention will make it much easier to transition to field overloading later.

@StefanKarpinski I think you'll have to make the decision if you want this thread to give a result before 1.0. ;-)

Sometimes, using get... and set... helps to avoid name collisions:

julia> using DataFrames

julia> data = DataFrame(A=[1,2], B=[3,4])
2ร—2 DataFrames.DataFrame
โ”‚ Row โ”‚ A โ”‚ B โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”ค
โ”‚ 1   โ”‚ 1 โ”‚ 3 โ”‚
โ”‚ 2   โ”‚ 2 โ”‚ 4 โ”‚

julia> names(data)
2-element Array{Symbol,1}:
 :A
 :B

julia> names = ["Julia", "R", "Python"]
WARNING: imported binding for names overwritten in module Main
3-element Array{String,1}:
 "Julia" 
 "R"     
 "Python"

julia> names(data)
ERROR: MethodError: objects of type Array{String,1} are not callable
Use square brackets [] for indexing an Array.

Yes, sometimes I wish I would be possible to have variables and functions under the same name, but this can't happen because the value of a variable can be a function. (And it might be confusing to read the code). With the specific names() example I am struggling in NamedArrays constructors where I tend to want to call the variable containing the names names, but I sometimes also need acces to the function names()---although this could happen via NamedArrays.names() I suppose.

Name collision between local variables and functions can be avoided by prefixing the module name of function; you can always use Base.names to mean the names function exported from Base unless you assign Base to something else.

I'm inclined to think setxxx! is a better convention than xxx! as @simonbyrne pointed above (https://github.com/JuliaLang/julia/issues/16770#issuecomment-250758845). But eventually we will need some standard getter/setter system in the language because these names easily conflict with names from other packages.

There is a point to be made for function names being (based on) verbs, which would indicate to prefer setxxx!. Function names being (based on) a noun usually imply a "get" or "compute", e.g. mean, so it is less of an argument in favor of getxxx instead of just xxx.

Also, without the set, there is some semantic ambiguity: Does xxx!(y,z) set the xxx property of y to z or does it store the xxx property of z in y?

object.fieldname is the nicer than fieldname(object) or get_fieldname(object) . Maybe have object.fieldname (or object$fieldname) being a call to getpublicfield and object..fieldname being the actual getfield could be a good option. In that way, types should define getpublicfield instead of getters...

That discussion is likely better to have in https://github.com/JuliaLang/julia/issues/1974.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

m-j-w picture m-j-w  ยท  3Comments

StefanKarpinski picture StefanKarpinski  ยท  3Comments

TotalVerb picture TotalVerb  ยท  3Comments

sbromberger picture sbromberger  ยท  3Comments

manor picture manor  ยท  3Comments