8

In R you can write functions that allow arguments to be unquoted attributes of a pre-defined object. For example, the interface to the DataFrame object allows the following:

# df has columns "A" and "B"
df = mutate(df, C=A*B)

Now df has a new column "C" that is the product of columns "A" and "B".

There is also the "formula" type which is unquoted:

lm(data=df, A~B)

This "Non-Standard Evaluation."

Is it fundamentally possible to do something similar in Javascript or Python.

Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294
abalter
  • 9,663
  • 17
  • 90
  • 145
  • 1
    I changed your last line to match the title ("is it possible" rather than "is it impossible") so that any answers that start with *yes* or *no* will be clear. – Gregor Thomas Jun 17 '19 at 03:48
  • Somebody asked a very similar question earlier today. The answer is basically "no". In JavaScript, expression evaluation works the way it works in all cases. – Pointy Jun 17 '19 at 03:51
  • Borderline separate question: Is there a reason it is "bad" to have this sort of "non standard evaluation" or is it just not a priority. – abalter Jun 17 '19 at 04:14
  • 1
    That's certainly a separate question. One major downside is that it is difficult to program with. Works great for interactive use, but it's not at all obvious how to turn things into variables. `dplyr` was originally built using [`lazyeval`](https://cran.r-project.org/web/packages/lazyeval/vignettes/lazyeval-old.html), at which point I remember a certain R guru saying something along the lines of "I've finally figured out The Right Way to do NSE. Turned out it had fundamental issues, and `dplyr` underwent a major re-write to switch to `rlang` underneath... – Gregor Thomas Jun 17 '19 at 11:58
  • It's complex enough that programming with dplyr [has it's own vignette](https://cran.r-project.org/web/packages/dplyr/vignettes/programming.html), but that is just a start. [This R-FAQ about programming with dplyr](https://stackoverflow.com/questions/26003574/dplyr-mutate-use-dynamic-variable-names) has about 50 duplicates, and I'm sure many more that aren't linked. Looking at the top answer, you can see 3 iterations depending which version of `dplyr` you are using, and as expressive as I do find `dplyr` generally, I can't really say I love expressions like `!!varname := Petal.Width`. – Gregor Thomas Jun 17 '19 at 12:03
  • Correction to comment above: I think `dplyr` started *before* `lazyeval`, was rewritten to use `lazyeval`, and then was rewritten again. None of these decisions were undertaken lightly, I'm sure, but it shows the complexity under the hood, and each version resulted in breaking changes to the interface for anyone using `dplyr` programmatically. – Gregor Thomas Jun 17 '19 at 12:05
  • @Gregor -- that explains a lot about my experiences with `dplyr`! I do love it though... – abalter Jun 17 '19 at 13:35
  • @ablter Would you consider accepting my answer? – pietrodito Jul 27 '22 at 11:43

2 Answers2

5

No, it is not possible to have NSE in Python and JavaScript.


Why is that? That is because in Python and JS, arguments are evaluated BEFORE they are passed to the function, while it is not the case in R.


Let's consider the two similar codes in R and Python:

main.R

enthusiastic_print <- function(x) {
   print("Welcome!")
   print(x)
}

enthusiastic_print("a" + 3)

main.py

def enthusiastic_print(x):
   print("Welcome!")
   print(x)
}

enthusiastic_print("a" + 3)

They will both procude an error. But now, let's have a look at when the error occurs:

.R

[1] "Welcome!"
Error in "a" + 3 : non-numeric argument to binary operator
exit status 1

.py

Traceback (most recent call last):
  File "main.py", line 6, in <module>
    enthusiastic_print("a" + 3)
TypeError: can only concatenate str (not "int") to str

You can see that Python evaluates what is passed to the function BEFORE the call. While R, keeps the complete expression passed as argument and evaluates it only when it is necessary.


You can even write this code in R that will produce NO error:

foo <- function(x) {
  print("Welcome in foo!")
  print("I never mess with args...")
}

foo("a" + 3)

You can also capture what was passed as argument and not evaluate it:

verbatim_arg <- function(x) {
  print(substitute(x))
}
  
verbatim_arg("a" + 3)

which produces:

"a" + 3

Finally, that's the functions substitute() combined with eval() which permit to use formulas and all the other NSE stuff.


Supplementary:

You can do the same test in node.js

function enthusiastic_print(x) {
  console.log("Welcome!")
  console.log(x)
}

enthusiastic_print("a".notAMethod())
pietrodito
  • 1,783
  • 15
  • 24
0

Well, the sympy library kind of does something similar in python, by letting you define placeholder variables which will only be evaluated later.

matthias
  • 109
  • 1
  • 6
  • 1
    Using sympy, you declare symbols (eg x,y) and then you use them to create objects (eg p = x**2 + y). But it does not work with NSE at all. – pietrodito Apr 04 '21 at 21:24