Re: [Rd] A trap for young players with the lapply() function.
On 29/03/17 20:32, Enrico Schumann wrote: (inline) On Tue, 28 Mar 2017, Rolf Turner writes: On 28/03/17 04:21, Barry Rowlingson wrote: On Mon, Mar 27, 2017 at 1:17 AM, Rolf Turnerwrote: Is there any way to trap/detect the use of an optional argument called "X" and thereby issue a more perspicuous error message? This would be helpful to those users who, like myself, are bears of very little brain. Failing that (it does look impossible) You can get the names of named arguments: > z = function(x,X){cos(x*X)} > names(formals(z)) [1] "x" "X" That doesn't seem to help. I tried putting a browser inside lapply() and looked at formals(FUN). This gave NULL, because FUN has already been taken to be the list argument "x" to which lapply() is being applied. You can get the call, without the arguments being matched, with `sys.call`. In your call of lapply, saying `sys.call()` before anything else would give you lapply(y, function(x, X) { cos(x * X) }, X = 2 * pi) which would allow you to get at the argument names of your function. if ("X" %in% names(sys.call()[[3L]][[2L]])) warnings("found 'X'") But more would be needed: you need to figure out which argument you actually meant to be FUN. (In the above, I simply assumed it was the second passed argument.) That is, you would need to figure out which passed argument is a function, which is not safe either, since ... may also contain functions. This idea appears to work for the particular example that I used, but it is not clear to me how to make it work in general. E.g. if we define foo <- function(x,X){X*x} and then do lapply(xxx,foo,X=2*pI) we find that sys.call[[3]] is of length 1 and consists only of the *name* "foo". One can then inspect formals(get(as.character(sys.call[[3]]))) and find "X" therein, on the basis of which to trigger a warning. However I don't know how to approach distinguish the two cases, or how to discern if there may be other cases lurking in the bushes. So the problem still looks insoluble to me. cheers, Rolf -- Technical Editor ANZJS Department of Statistics University of Auckland Phone: +64-9-373-7599 ext. 88276 __ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
Re: [Rd] A trap for young players with the lapply() function.
(inline) On Tue, 28 Mar 2017, Rolf Turner writes: > On 28/03/17 04:21, Barry Rowlingson wrote: >> On Mon, Mar 27, 2017 at 1:17 AM, Rolf Turnerwrote: >>> >>> Is there any way to trap/detect the use of an optional argument called >>> "X" and thereby issue a more perspicuous error message? >>> >>> This would be helpful to those users who, like myself, are bears of very >>> little brain. >>> >>> Failing that (it does look impossible) >> >> You can get the names of named arguments: >> >> > z = function(x,X){cos(x*X)} >> > names(formals(z)) >> [1] "x" "X" > > That doesn't seem to help. I tried putting a browser inside lapply() > and looked at formals(FUN). This gave NULL, because FUN has already > been taken to be the list argument "x" to which lapply() is being > applied. You can get the call, without the arguments being matched, with `sys.call`. In your call of lapply, saying `sys.call()` before anything else would give you lapply(y, function(x, X) { cos(x * X) }, X = 2 * pi) which would allow you to get at the argument names of your function. if ("X" %in% names(sys.call()[[3L]][[2L]])) warnings("found 'X'") But more would be needed: you need to figure out which argument you actually meant to be FUN. (In the above, I simply assumed it was the second passed argument.) That is, you would need to figure out which passed argument is a function, which is not safe either, since ... may also contain functions. >>> might it not be a good idea to >>> add a warning to the help for lapply(), to the effect that if FUN has an >>> optional argument named "X" then passing this argument via "..." will >>> cause this argument to be taken as the first argument to lapply() and >>> thereby induce an error? >> >> Another idea might be to use purrr:map instead, which is quite happy >> with X in your function: >> >> > xxx <- purrr::map(y,function(x,X){cos(x*X)},X=2*pi) >> > xxx >> [[1]] >> [1] 0.08419541 >> >> [[2]] >> [1] 0.6346404 >> >> [[3]] >> [1] 0.9800506 >> >> [[4]] >> [1] 0.8686734 >> >> [[5]] >> [1] -0.9220073 >> >> But don't feed `.x` to your puing cats, or fails silently: >> >> > xxx <- purrr::map(y,function(x,.x){cos(x*.x)},.x=2*pi) >> > xxx >> [[1]] >> NULL >> >> But who would have a function with `.x` as an argument? > > Indeed. It struck me that a possible workaround would be to change > the name of the first argument of lapply() from "X" to ".X". No-one > would have a function with an argument names ".X" --- at least I > wouldn't, so this would solve the problem for me. > > It seems to me that this change could be made without breaking anything. > But perhaps I am engaging in my usual PollyAnna-ish optimism! :-) > > cheers, > > Rolf -- Enrico Schumann Lucerne, Switzerland http://enricoschumann.net __ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
Re: [Rd] A trap for young players with the lapply() function.
On 29/03/17 11:03, William Dunlap wrote: I think that the suggestion I made, in response to a posting by Barry Rowlingson, that the first argument of lapply() be given the name of ".X" rather than just-plain-X, would be (a) effective, and (b) harmless. It would break any call to *apply() that used X= to name the first argument. There are currently 3020 such calls in the R code in CRAN. Okay. Scratch that idea. One can avoid the problem by creating the function given as the FUN argument when one calls lapply() and the like. Don't give that function arguments named "X", "FUN", "USE.NAMES", etc. and perhaps make use of R's lexical scoping to avoid having to use many arguments to the function. E.g., instead of sapply(1:5, sin) use sapply(1:5, function(theta) sin(theta)) or instead of myY <- 3 sapply(1:5, atan2, y=myY) use myY <- 3 sapply(1:5, function(x) atan2(myY, x)) Again, all very sound advice but it does not address the problem of there being a trap for young players. The advice can only be applied by a user only if the user is *aware* of the trap. At this point it would appear that the problem is fundamentally unsolvable. :-( cheers, Rolf -- Technical Editor ANZJS Department of Statistics University of Auckland Phone: +64-9-373-7599 ext. 88276 __ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
Re: [Rd] A trap for young players with the lapply() function.
>I think that the suggestion I made, in response to a posting by Barry >>Rowlingson, that the first argument of lapply() be given the name of ".X" >rather >than just-plain-X, would be (a) effective, and (b) harmless. It would break any call to *apply() that used X= to name the first argument. There are currently 3020 such calls in the R code in CRAN. One can avoid the problem by creating the function given as the FUN argument when one calls lapply() and the like. Don't give that function arguments named "X", "FUN", "USE.NAMES", etc. and perhaps make use of R's lexical scoping to avoid having to use many arguments to the function. E.g., instead of sapply(1:5, sin) use sapply(1:5, function(theta) sin(theta)) or instead of myY <- 3 sapply(1:5, atan2, y=myY) use myY <- 3 sapply(1:5, function(x) atan2(myY, x)) Bill Dunlap TIBCO Software wdunlap tibco.com On Tue, Mar 28, 2017 at 2:30 PM, Rolf Turnerwrote: > On 28/03/17 15:26, Charles C. Berry wrote: >> >> On Mon, 27 Mar 2017, Rolf Turner wrote: >> >>> >>> From time to time I get myself into a state of bewilderment when using >>> apply() by calling it with FUN equal to a function which has an >>> "optional" argument named "X". >>> >>> E.g. >>> >>>xxx <- lapply(y,function(x,X){cos(x*X)},X=2*pi) >>> >>> which produces the error message >>> Error in get(as.character(FUN), mode = "function", envir = envir) : object 'y' of mode 'function' was not found >>> >>> >>> This of course happens because the name of the first argument of >>> lapply() is "X" and so it takes the value of this first argument to be >>> the supplied X (2*pi in the foregoing example) and then expects what >>> the user has denoted by "y" to be the value of FUN, and (obviously!) >>> it isn't. >>> >> >> The lapply help page addresses this issue in `Details' : >> >> "it is good practice to name the first two arguments X and FUN if ... is >> passed through: this both avoids partial matching to FUN and ensures >> that a sensible error message is given if arguments named X or FUN are >> passed through ..." >> >> So that advice suggests something like: >> >> xxx <- lapply( X=y, FUN=function(X,x){cos(X*x)}, x=2*pi ) > > > > That is of course very sound advice, but it pre-supposes that the user is > *aware* that there is a pitfall to be avoided. I was hoping for something > that would protect dweebs like myself from the pitfall given that we are too > obtuse to be cognizant of its existence. > > I think that the suggestion I made, in response to a posting by Barry > Rowlingson, that the first argument of lapply() be given the name of ".X" > rather than just-plain-X, would be (a) effective, and (b) harmless. > > cheers, > > Rolf > > -- > Technical Editor ANZJS > Department of Statistics > University of Auckland > Phone: +64-9-373-7599 ext. 88276 > > __ > R-devel@r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel __ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
Re: [Rd] A trap for young players with the lapply() function.
On 28/03/17 15:26, Charles C. Berry wrote: On Mon, 27 Mar 2017, Rolf Turner wrote: From time to time I get myself into a state of bewilderment when using apply() by calling it with FUN equal to a function which has an "optional" argument named "X". E.g. xxx <- lapply(y,function(x,X){cos(x*X)},X=2*pi) which produces the error message Error in get(as.character(FUN), mode = "function", envir = envir) : object 'y' of mode 'function' was not found This of course happens because the name of the first argument of lapply() is "X" and so it takes the value of this first argument to be the supplied X (2*pi in the foregoing example) and then expects what the user has denoted by "y" to be the value of FUN, and (obviously!) it isn't. The lapply help page addresses this issue in `Details' : "it is good practice to name the first two arguments X and FUN if ... is passed through: this both avoids partial matching to FUN and ensures that a sensible error message is given if arguments named X or FUN are passed through ..." So that advice suggests something like: xxx <- lapply( X=y, FUN=function(X,x){cos(X*x)}, x=2*pi ) That is of course very sound advice, but it pre-supposes that the user is *aware* that there is a pitfall to be avoided. I was hoping for something that would protect dweebs like myself from the pitfall given that we are too obtuse to be cognizant of its existence. I think that the suggestion I made, in response to a posting by Barry Rowlingson, that the first argument of lapply() be given the name of ".X" rather than just-plain-X, would be (a) effective, and (b) harmless. cheers, Rolf -- Technical Editor ANZJS Department of Statistics University of Auckland Phone: +64-9-373-7599 ext. 88276 __ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
Re: [Rd] A trap for young players with the lapply() function.
On Mon, 27 Mar 2017, Rolf Turner wrote: From time to time I get myself into a state of bewilderment when using apply() by calling it with FUN equal to a function which has an "optional" argument named "X". E.g. xxx <- lapply(y,function(x,X){cos(x*X)},X=2*pi) which produces the error message Error in get(as.character(FUN), mode = "function", envir = envir) : object 'y' of mode 'function' was not found This of course happens because the name of the first argument of lapply() is "X" and so it takes the value of this first argument to be the supplied X (2*pi in the foregoing example) and then expects what the user has denoted by "y" to be the value of FUN, and (obviously!) it isn't. The lapply help page addresses this issue in `Details' : "it is good practice to name the first two arguments X and FUN if ... is passed through: this both avoids partial matching to FUN and ensures that a sensible error message is given if arguments named X or FUN are passed through ..." So that advice suggests something like: xxx <- lapply( X=y, FUN=function(X,x){cos(X*x)}, x=2*pi ) Best, Chuck __ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
Re: [Rd] A trap for young players with the lapply() function.
On 28/03/17 04:21, Barry Rowlingson wrote: On Mon, Mar 27, 2017 at 1:17 AM, Rolf Turnerwrote: Is there any way to trap/detect the use of an optional argument called "X" and thereby issue a more perspicuous error message? This would be helpful to those users who, like myself, are bears of very little brain. Failing that (it does look impossible) You can get the names of named arguments: > z = function(x,X){cos(x*X)} > names(formals(z)) [1] "x" "X" That doesn't seem to help. I tried putting a browser inside lapply() and looked at formals(FUN). This gave NULL, because FUN has already been taken to be the list argument "x" to which lapply() is being applied. might it not be a good idea to add a warning to the help for lapply(), to the effect that if FUN has an optional argument named "X" then passing this argument via "..." will cause this argument to be taken as the first argument to lapply() and thereby induce an error? Another idea might be to use purrr:map instead, which is quite happy with X in your function: > xxx <- purrr::map(y,function(x,X){cos(x*X)},X=2*pi) > xxx [[1]] [1] 0.08419541 [[2]] [1] 0.6346404 [[3]] [1] 0.9800506 [[4]] [1] 0.8686734 [[5]] [1] -0.9220073 But don't feed `.x` to your puing cats, or fails silently: > xxx <- purrr::map(y,function(x,.x){cos(x*.x)},.x=2*pi) > xxx [[1]] NULL But who would have a function with `.x` as an argument? Indeed. It struck me that a possible workaround would be to change the name of the first argument of lapply() from "X" to ".X". No-one would have a function with an argument names ".X" --- at least I wouldn't, so this would solve the problem for me. It seems to me that this change could be made without breaking anything. But perhaps I am engaging in my usual PollyAnna-ish optimism! :-) cheers, Rolf -- Technical Editor ANZJS Department of Statistics University of Auckland Phone: +64-9-373-7599 ext. 88276 __ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
Re: [Rd] A trap for young players with the lapply() function.
On Mon, Mar 27, 2017 at 1:17 AM, Rolf Turnerwrote: > > Is there any way to trap/detect the use of an optional argument called > "X" and thereby issue a more perspicuous error message? > > This would be helpful to those users who, like myself, are bears of very > little brain. > > Failing that (it does look impossible) You can get the names of named arguments: > z = function(x,X){cos(x*X)} > names(formals(z)) [1] "x" "X" > might it not be a good idea to > add a warning to the help for lapply(), to the effect that if FUN has an > optional argument named "X" then passing this argument via "..." will > cause this argument to be taken as the first argument to lapply() and > thereby induce an error? Another idea might be to use purrr:map instead, which is quite happy with X in your function: > xxx <- purrr::map(y,function(x,X){cos(x*X)},X=2*pi) > xxx [[1]] [1] 0.08419541 [[2]] [1] 0.6346404 [[3]] [1] 0.9800506 [[4]] [1] 0.8686734 [[5]] [1] -0.9220073 But don't feed `.x` to your puing cats, or fails silently: > xxx <- purrr::map(y,function(x,.x){cos(x*.x)},.x=2*pi) > xxx [[1]] NULL But who would have a function with `.x` as an argument? > __ > R-devel@r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel __ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
[Rd] A trap for young players with the lapply() function.
From time to time I get myself into a state of bewilderment when using apply() by calling it with FUN equal to a function which has an "optional" argument named "X". E.g. xxx <- lapply(y,function(x,X){cos(x*X)},X=2*pi) which produces the error message Error in get(as.character(FUN), mode = "function", envir = envir) : object 'y' of mode 'function' was not found This of course happens because the name of the first argument of lapply() is "X" and so it takes the value of this first argument to be the supplied X (2*pi in the foregoing example) and then expects what the user has denoted by "y" to be the value of FUN, and (obviously!) it isn't. Once one realises what is going on, it's all quite obvious, and usually pretty easy to fix. OTOH there are lots of functions around with second or third arguments whose formal name is "X", and these can trip one up until the penny drops. This keeps happening to me, over and over again (with sufficiently long intervals between occurrences so that my ageing memory forgets the previous occurrence). Is there any way to trap/detect the use of an optional argument called "X" and thereby issue a more perspicuous error message? This would be helpful to those users who, like myself, are bears of very little brain. Failing that (it does look impossible) might it not be a good idea to add a warning to the help for lapply(), to the effect that if FUN has an optional argument named "X" then passing this argument via "..." will cause this argument to be taken as the first argument to lapply() and thereby induce an error? cheers, Rolf Turner -- Technical Editor ANZJS Department of Statistics University of Auckland Phone: +64-9-373-7599 ext. 88276 __ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel