Skip to content

anonymous function parsing bug?

10 messages · William Dunlap, Wilm Schumacher, Luke Tierney +1 more

#
Hi,

I hope this is the correct list for my question. I found a wired 
behaviour of my R installation on the evaluation of anonymous functions.

minimal working example

###
f<-function(x) {
     print( 2*x )
}(2)

class(f)

f(3)

f<-function(x) {
     print( 2*x )
}(4)(5)

f(6)
###

leads to

###
 > f<-function(x) {
+ print( 2*x )
+ }(2)
 >
 > class(f)
[1] "function"
 >
 > f(3)
[1] 6
Error in f(3) : attempt to apply non-function
 >
 > f<-function(x) {
+ print( 2*x )
+ }(4)(5)
 >
 > f(6)
[1] 12
Error in f(6) : attempt to apply non-function

###

is this a bug or desired behavior? Using parenthesis of coures solves 
the problem. However, I think the operator precedence could be the 
problem here. I looked at the "./src/main/gram.y" and I think that the 
line 385
     |    FUNCTION '(' formlist ')' cr expr_or_assign %prec LOW
should be of way higher precedence. But I cannot forsee the side effects 
of that (which could be horrible in that case).

If this is the desired behaviour and not a bug, I'm very interested in 
the rational behind that.

Best wishes,

Wilm

ps:

$ R --version
R version 3.3.1 (2016-06-21) -- "Bug in Your Hair"
#
Hi,

sry for the double posting. I forgot to mention that this example

###
f<-function(x) {
     return( 2*x )
}(2)

class(f)

f(3)

f<-function(x) {
     return( 2*x )
}(4)(5)

f(6)
###

leads to

##
 > f<-function(x) {
+     return( 2*x )
+ }(2)
 >
 > class(f)
[1] "function"
 >
 > f(3)
[1] 6
 >
 > f<-function(x) {
+     return( 2*x )
+ }(4)(5)
 >
 > f(6)
[1] 12
##

which is even stranger (at least for me) and contradicts the first 
listing imho in behaviour.

Best wishes,

Wilm

Am 21.10.2016 um 15:10 schrieb Wilm Schumacher:
#
Here is a simplified version of your problem
  > { sqrt }(c(2,4,8))
  [1] 1.414214 2.000000 2.828427
Do you want that to act differently?


Bill Dunlap
TIBCO Software
wdunlap tibco.com

On Fri, Oct 21, 2016 at 6:10 AM, Wilm Schumacher <wilm.schumacher at gmail.com>
wrote:

  
  
#
Hi,

thx for the reply. Unfortunately that is not a simplified version of the 
problem. You have a function, call it and get the result (numeric in, 
numeric out in that case). For simplicity lets use the "return" case:

##
foobar<-function(x) { return(sqrt(x)) }(2)
##
which is a function (numeric in, numeric out) which is defined, then 
gets called and the return value is a function (with an appendix of 
"(2)" which gets ignored), not the numeric.

In my opinion the result of the expression above should be a numeric 
(1.41... in this case) or an parser error because of ambiguities.

e.g. in comparison with node.js

##
function(x){
     return(2*x)
}(2);
##

leads to

##
SyntaxError: Unexpected token (
##

Or Haskell (and basically every complete functional languange)
##
(\x -> 2*x) 2
##
which leads to 4 (... okay, that is not comparable because here the 
parenthesis make a closure which also works in R or node.js).

However, I think it's weird that

 > ( function(x) { return(2*x) } ( 2 ) ) (3)

is a legal statement which results to 6 and that the "(2)" is basically 
ignored by the parser.

Furthermore it is very strange, that

##
f1<-function(x) { print(2*x) }(2)
f1(3)
##
does the command and gives an error ("attempt to apply non-function") and
##
f2<-function(x) { return(2*x) }(2)
f2(3)
##
is perfectly fine. Thus the return statement changes the interpretation 
as a function? Or do I miss something?

Best wishes

Wilm

Am 21.10.2016 um 17:00 schrieb William Dunlap:

  
  
#
You might find it useful to look at what body() shows you for your
example and to think about what return does.

Best,

luke
On Fri, 21 Oct 2016, Wilm Schumacher wrote:

            

  
    
#
Are you saying that
    f1 <- function(x) log(x)
    f2 <- function(x) { log } (x)
should act differently?

Using 'return' complicates the matter, because it affects evaluation, not
parsing.

Bill Dunlap
TIBCO Software
wdunlap tibco.com

On Fri, Oct 21, 2016 at 8:43 AM, Wilm Schumacher <wilm.schumacher at gmail.com>
wrote:

  
  
#
Hi,


Am 21.10.2016 um 18:10 schrieb William Dunlap:
yes. Or more precisely: I would expect that. "Should" implies, that I 
want to change something. I just want to understand the behavior (or 
file a bug, if this would have been one).

As I wrote, in e.g. node.js the pendents to the lines that you wrote are 
treated differently (the first is a function, the latter is a parsing 
error).

Let's use this example instead:
x <- 20
f1 <- function(x) { x<-x+1; log(x) }
f2 <- function(x) { x<-x+1; log } (x)
which act equally.

But as the latter is a legal statement, I would read it as
f2 <- (function(x) { x<-x+1; log }) (x)

thus, I would expect the first to be a function, the latter to be a 
numeric ( log(20) in this case ).
But perhaps it illustrates my problem a little better:
x <- 20
f1 <- function(x) return(log(x))
f2 <- function(x) { return(log) } (x)

f1(10) is a numeric, f2(10) is the log function. Again: as the latter is 
a legal statement, I would expect:
f2 <- (function(x) { x<-x+1; log }) (x)

However, regarding the answers I will try to construct the AST regarding 
the grammar defined in gramm.y of that statement
f2 <- function(x) { x<-x+1; log } (x)
to understand what the R interpreter really does.

Best wishes,

Wilm
#
Am 21.10.2016 um 18:10 schrieb William Dunlap:
f1 <- function(x) log(x)

    f2 <- function(x) { log } (x)

should act differently?

yes.


But that would mean that {log} would act differently than log.
I suppose it is a matter of taste, but I say yuck.

As for 'return', don't use it if you want readable code.  It is
like a goto but worse.  It is never necessary.


Bill Dunlap
TIBCO Software
wdunlap tibco.com

On Fri, Oct 21, 2016 at 10:17 AM, Wilm Schumacher <wilm.schumacher at gmail.com

  
  
#
I think Bill and Luke are failing in trying to make you work out the logic for yourself...

The point is that 
{
  some_computation
}(x)

is an expression that evaluates some_computation and applies it as a function to the argument x (or fails if not a function). 

When you define functions, the body can be a single expression, so

f <- function(a)
{
  some_computation
}(x)

is effectively the same as

f <- function(a) {
 {
   some_computation
 }(x)
}

where you seem to be expecting

{f <- function(a) {
 {
   some_computation
 }
}(x)

Got it?
#
On Fri, 21 Oct 2016, William Dunlap via R-devel wrote:

            
As a rule I agree, but one case where return is clearer than the alternative is

repeat {
    ....
    if (...)
       return(...)
}

Complicated nested if expressions are also sometimes clearer using
return as an early breakout.

Best,

luke