style icon indicating copy to clipboard operation
style copied to clipboard

Add style for constants?

Open joshwlambert opened this issue 1 year ago • 5 comments

Although fairly uncommon in R, internal and exported constants are used in R packages (and base R e.g. ?pi). Would it be beneficial to add a short piece of advice on how to style these?

I've had a very quick look at {lintr} and couldn't see anything on this, but definitely could have missed it.

PEP8 advises all caps if interested in what other language style guides say.

joshwlambert avatar Oct 31 '24 15:10 joshwlambert

In typical R interactive sessions, all variables declared by the user reside in the global environment, making it unnecessary to style them differently based on namespaces.

The concept of constant variables in R is meaningful only within environments. Attempting to enforce constants through naming conventions is inefficient.

The R project provides specific documentation on namespacing in packages. Variables declared within package namespaces are sealed and should not be modified. ^1

Assigning variables to the global environment from within functions is considered bad practice. ^2

If your variable is a data frame or any form of data inside a package, use the data mechanisms and document it accordingly. ^3

When writing functions that interact with variables exported from other packages or within their namespaces, namespace them appropriately (e.g., base::pi).

Consider using explicit environments if you need to maintain the state within your functions. ^4

luciorq avatar Nov 02 '24 22:11 luciorq

I agree with your points @luciorq, perhaps I was unclear in my original comment as I think I'm thinking of a much more restricted meaning of constants. I am referring to variables that are exported by a package and can be accessed via the package namespace (but for the purpose of this discussion styling would also apply to internal constants within a package).

The exported constant cannot be modified e.g.,

base::pi = 3
#> Error in base::pi = 3: object 'base' not found

Created on 2024-11-15 with reprex v2.1.0

The aspect of styling is mainly to differentiate between functions and variables/constants exported by a package, or in the case of internal constants to help see when a piece of code is using a constant (as opposed to a variable defined elsewhere or input into a function).

As this is a relatively uncommon feature of R this issue can be closed if deemed irrelevant.

joshwlambert avatar Nov 15 '24 16:11 joshwlambert

The exported constant cannot be modified e.g.,

e = baseenv()
unlockBinding("pi", e)
assign("pi", exp(1), e)
lockBinding("pi", e)
base::pi
# [1] 2.718282

🙃

MichaelChirico avatar Nov 15 '24 16:11 MichaelChirico

I was referring to modification by direct assignment, but wasn't familiar with unlockBinding() and lockBinding() so appreciate them being pointed out.

If the argument is variables (or referred to in my previous comments as constants) can always be modified by some mechanism in R and therefore they shouldn't be styled, this is a valid viewpoint, although I still view styling them as helpful in my own experience. Either way documenting the style or lack thereof could be beneficial to package developers.

joshwlambert avatar Nov 15 '24 17:11 joshwlambert

Hello,

I am also very much in favor of a style guide specifically for constants or values meant to be constant. OP referred to PEP8 in python. Constants are not enforced in Python (you can re-assign a constant). But the specific styling (all caps) let the user knows the intended purpose. It helps with readability a lot, and make it clear that it's expected to be read-only.

I would suggest that the styling could consider allcaps variables as constants, and flag any re-assignment attempts in the code as bad practice. Similar to const in JavaScript, or const in Java, modifying the internal state of an object is still allowed, but not reassignment.

# Good
COLORS <- c("Control" = "black", "Treatment" = "red")
COLORS["Random"] = "grey"

# Bad
COLORS <- c("Control" = "black", "Treatment" = "red")
COLORS <- c("Control" = "black", "Treatment" = "red", "Random" = "grey")

Edit: As @MichaelChirico pointed out, there are numerous ways of evading a linter, but if someone's goal is to find workaround to not follow linter's guideline while checking for linter's guideline, I mean... ok that's some kind of interesting challenge, but all linters rely on the fact that you are willingly complying to its recommendation.

dimitri-fabreges avatar Nov 04 '25 09:11 dimitri-fabreges