Evaluating lazily 'f<-' ?
In your case, yes, there is a negative impact to formatting the code like:
x <- `padding<-`(right(x), ...)
and it comes from using 'substitute' without specifying an environment / /
list. It's my biggest problem with the tidyverse packages, the use of
non-standard evaluation. 'substitute' was originally created for simple
things like getting informative labels for data sets and plots, as well as
for 'delayedAssign'. For example, you could write your function to use the
above standard syntax, something like:
`padding<-` <- function (x, ..., value)
{
sx <- substitute(x)
choices <- c("bottom", "left", "top", "right")
if (!is.call(sx) || !is.symbol(sx[[1L]]) ||
!(k <- match(as.character(sx[[1L]]), choices, nomatch = 0L)))
stop(gettextf("invalid 'x', must be a call to %s",
paste(sQuote(choices), collapse = ", ")))
choice <- choices[[k]]
if (length(sx) != 2L)
stop(gettextf("invalid 'x', %d arguments passed to '%s' which
requires 1",
length(sx) - 1L, choice))
x <- eval(sx[[2L]], parent.frame())
# do whatever else with x, choice, ..., value
}
but given that you cannot use it like
padding(right(x)) <- 5
I can't say that I recommend it. Additionally, even if you define
`padding<-` like this, because of the non-standard evaluation from
'substitute', it means you CAN'T make a wrapper function for `padding<-`:
wrapper <- function (x, ..., value)
{
# not sure exactly what this wrapper should do,
# we'll add another element to 'value'
`padding<-`(x = x, ..., value = c(list(value), "test"))
}
this won't work for a case like:
wrapper(right(letters))
because now, when you do 'substitute', it will give you the literal symbol
'x', not really what you wanted, because of the non-standard evaluation. Of
course, do whatever you want to make the syntax look the way you want, but
I think you're going about this the wrong way. I think the 'which' argument
I previously suggested will serve you far better. I hope this helps.
On Wed, Sep 15, 2021 at 2:26 AM Leonard Mada <leo.mada at syonic.eu> wrote:
Hello Andrew,
On 9/15/2021 6:53 AM, Andrew Simmons wrote:
names(x) <- c("some names")
if different from
`names<-`(x, value = c("some names"))
because the second piece of code does not ever call `<-`. The first piece
of code is (approximately) equivalent to
`*tmp*` <- x
`*tmp*` <- `names<-`(`*tmp*`, value = c("some names"))
x <- `*tmp*`
This is my question:
Would there be any negative impact if the code is changed to:
x <- 'names<-'(x, value=...);
The "x" will be evaluated inside 'names<-' and this function outputs &
assigns "x". The "creation" of "x" in the current environment is done only
after the call to 'names<-' (if it did not exist before).
Leonard
Another example,
y <- `names<-`(x, value = c("some names"))
now y will be equivalent to x if we did
names(x) <- c("some names")
except that the first will not update x, it will still have its old names.
On Mon, Sep 13, 2021 at 4:33 PM Leonard Mada <leo.mada at syonic.eu> wrote:
On 9/13/2021 11:28 PM, Andrew Simmons wrote:
In the example you gave : r(x) <- 1
r(x) is never evaluated, the above calls `r<-`,
in fact r does not even have to be an existing function.
I meant:
'*tmp*' <- x; # "x" is evaluated here;
'r<-' is called after this step, which makes sense in the case of
subsetting;
But I am wondering if changing this behaviour, when NO subsetting is
performed, would have any impact.
e.g. names(x) = c("some names");
# would it have any impact to skip the evaluation of "x" and call
directly:
'names<-'(x, value);
Leonard
On Mon, Sep 13, 2021, 16:18 Leonard Mada <leo.mada at syonic.eu> wrote:
Hello,
I have found the evaluation: it is described in the section on
subsetting. The forced evaluation makes sense for subsetting.
On 9/13/2021 9:42 PM, Leonard Mada wrote:
Hello Andrew,
I try now to understand the evaluation of the expression:
e = expression(r(x) <- 1)
# parameter named "value" seems to be required;
'r<-' = function(x, value) {print("R");}
eval(e, list(x=2))
# [1] "R"
# both versions work
'r<-' = function(value, x) {print("R");}
eval(e, list(x=2))
# [1] "R"
### the Expression
e[[1]][[1]] # "<-", not "r<-"
e[[1]][[2]] # "r(x)"
The evaluation of "e" somehow calls "r<-", but evaluates also the
argument of r(...). I am still investigating what is actually happening.
The forced evaluation is relevant for subsetting, e.g.:
expression(r(x)[3] <- 1)
expression(r(x)[3] <- 1)[[1]][[2]]
# r(x)[3] # the evaluation details are NOT visible in the expression per
se;
# Note: indeed, it makes sens to first evaluate r(x) and then to perform
the subsetting;
However, in the case of a non-subsetted expression:
r(x) <- 1;
It would make sense to evaluate lazily r(x) if no subsetting is involved
(more precisely "r<-"(x, value) ).
Would this have any impact on the current code?
Sincerely,
Leonard
Sincerely,
Leonard
On 9/13/2021 9:15 PM, Andrew Simmons wrote:
R's parser doesn't work the way you're expecting it to. When doing an
assignment like:
padding(right(df)) <- 1
it is broken into small stages. The guide "R Language Definition" claims
that the above would be equivalent to:
`<-`(df, `padding<-`(df, value = `right<-`(padding(df), value = 1)))
but that is not correct, and you can tell by using `substitute` as you
were above. There isn't a way to do what you want with the syntax you
provided, you'll have to do something different. You could add a `which`
argument to each style function, and maybe put the code for `match.arg` in
a separate function:
match.which <- function (which)
match.arg(which, c("bottom", "left", "top", "right"), several.ok = TRUE)
padding <- function (x, which)
{
which <- match.which(which)
# more code
}
border <- function (x, which)
{
which <- match.which(which)
# more code
}
some_other_style <- function (x, which)
{
which <- match.which(which)
# more code
}
I hope this helps.
On Mon, Sep 13, 2021 at 12:17 PM Leonard Mada <leo.mada at syonic.eu>
wrote:
Hello Andrew,
this could work. I will think about it.
But I was thinking more generically. Suppose we have a series of
functions:
padding(), border(), some_other_style();
Each of these functions has the parameter "right" (or the group of
parameters c("right", ...)).
Then I could design a function right(FUN) that assigns the value to
this parameter and evaluates the function FUN().
There are a few ways to do this:
1.) Other parameters as ...
right(FUN, value, ...) = value; and then pass "..." to FUN.
right(value, FUN, ...) = value; # or is this the syntax? (TODO: explore)
2.) Another way:
right(FUN(...other parameters already specified...)) = value;
I wanted to explore this 2nd option: but avoid evaluating FUN, unless
the parameter "right" is injected into the call.
3.) Option 3:
The option you mentioned.
Independent of the method: there are still weird/unexplained behaviours
when I try the initial code (see the latest mail with the improved code).
Sincerely,
Leonard
On 9/13/2021 6:45 PM, Andrew Simmons wrote:
I think you're trying to do something like:
`padding<-` <- function (x, which, value)
{
which <- match.arg(which, c("bottom", "left", "top", "right"),
several.ok = TRUE)
# code to pad to each side here
}
Then you could use it like
df <- data.frame(x=1:5, y = sample(1:5, 5))
padding(df, "right") <- 1
Does that work as expected for you?
On Mon, Sep 13, 2021, 11:28 Leonard Mada via R-help <
r-help at r-project.org> wrote:
I try to clarify the code:
###
right = function(x, val) {print("Right");};
padding = function(x, right, left, top, bottom) {print("Padding");};
'padding<-' = function(x, ...) {print("Padding = ");};
df = data.frame(x=1:5, y = sample(1:5, 5)); # anything
### Does NOT work as expected
'right<-' = function(x, value) {
print("This line should be the first printed!")
print("But ERROR: x was already evaluated, which printed
\"Padding\"");
x = substitute(x); # x was already evaluated before substitute();
return("Nothing"); # do not now what the behaviour should be?
}
right(padding(df)) = 1;
### Output:
[1] "Padding"
[1] "This line should be the first printed!"
[1] "But ERROR: x was already evaluated, which printed \"Padding\""
[1] "Padding = " # How did this happen ???
### Problems:
1.) substitute(x): did not capture the expression;
- the first parameter of 'right<-' was already evaluated, which is not
the case with '%f%';
Can I avoid evaluating this parameter?
How can I avoid to evaluate it and capture the expression:
"right(...)"?
2.) Unexpected
'padding<-' was also called!
I did not know this. Is it feature or bug?
R 4.0.4
Sincerely,
Leonard
On 9/13/2021 4:45 PM, Duncan Murdoch wrote:
On 13/09/2021 9:38 a.m., Leonard Mada wrote:
Hello, I can include code for "padding<-"as well, but the error is before
that,
namely in 'right<-':
right = function(x, val) {print("Right");};
# more options:
padding = function(x, right, left, top, bottom) {print("Padding");};
'padding<-' = function(x, ...) {print("Padding = ");};
df = data.frame(x=1:5, y = sample(1:5, 5));
### Does NOT work
'right<-' = function(x, val) {
print("Already evaluated and also does not use 'val'");
x = substitute(x); # x was evaluated before
}
right(padding(df)) = 1;
It "works" (i.e. doesn't generate an error) for me, when I correct your typo: the second argument to `right<-` should be `value`, not `val`. I'm still not clear whether it does what you want with that fix, because I don't really understand what you want. Duncan Murdoch
I want to capture the assignment event inside "right<-" and then
call
the function padding() properly.
I haven't thought yet if I should use:
padding(x, right, left, ... other parameters);
or
padding(x, parameter) <- value;
It also depends if I can properly capture the unevaluated expression
inside "right<-":
'right<-' = function(x, val) {
# x is automatically evaluated when using 'f<-'!
# but not when implementing as '%f%' = function(x, y);
}
Many thanks,
Leonard
On 9/13/2021 4:11 PM, Duncan Murdoch wrote:
On 12/09/2021 10:33 a.m., Leonard Mada via R-help wrote:
How can I avoid evaluation?
right = function(x, val) {print("Right");};
padding = function(x) {print("Padding");};
df = data.frame(x=1:5, y = sample(1:5, 5));
### OK
'%=%' = function(x, val) {
x = substitute(x);
}
right(padding(df)) %=% 1; # but ugly
### Does NOT work
'right<-' = function(x, val) {
print("Already evaluated and also does not use 'val'");
x = substitute(x); # is evaluated before
}
right(padding(df)) = 1
That doesn't make sense. You don't have a `padding<-` function,
and
yet you are trying to call right<- to assign something to
padding(df).
I'm not sure about your real intention, but assignment functions by their nature need to evaluate the thing they are assigning to,
since
they are designed to modify objects, not create new ones. To create a new object, just use regular assignment. Duncan Murdoch
______________________________________________ R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see https://stat.ethz.ch/mailman/listinfo/r-help PLEASE do read the posting guide http://www.R-project.org/posting-guide.html and provide commented, minimal, self-contained, reproducible code.