Skip to content

sub question

11 messages · Peter Dalgaard, Gabor Grothendieck, Wacek Kusnierczyk

#
Gabor Grothendieck wrote:
th some additional boring pedantry wrt. ?gsubfn, which says:
They are still free variables, subject to the same rules of variable 
lookup. Wacek is right: The RHS is scanned recursively for objects of 
mode "name" _except_ when they appear as function names (i.e. if 
subexpression e is mode "call", then forget e[[1]] and look at the 
arguments in as.list(e)[-1]. Not sure if this also happens if e[[1]] is 
not a name, e.g. in f(a)(b), do you get both a and b or just b?)

He is also right that it is pedantry...
#
On Sun, Feb 1, 2009 at 6:44 AM, Peter Dalgaard <p.dalgaard at biostat.ku.dk> wrote:
Certainly that is one interpretation although from context it seems an unlikely
one since from that viewpoint +, [ and, in fact, everything is a variable.
#
Peter Dalgaard wrote:
a follow-up:

foo = function() bar()

is 'foo' in the expression above a variable or a function name (it is
free, anyway)?  (for sure, 'foo' is *not* a function, because here, and
in the above, it is *syntax* that is discussed, and functions are not
syntactic objects)
"Take time to consider.  The smallest point may be the most essential."
Sherlock Holmes, The Adventure of the red Circle

(and i actually quote it from a statistics textbook, written by people
who apparently think care for details matters.  well known
statisticians, for that matter.)

we can agree this is pedantry, i couldn't care less about what you call
it.  the problem is that you (meaning: some of the r folks posting here)
seem to regard care for details as something negative and undesirable,
and discard problems by simply tagging them as 'boring pedantry'.  and
then it's too late, nothing can be done without crashing much old code,
live with it, it's a user bug if your code crashes because obvious
assumptions are often wrong in r.

in this particular case, the issue at stake is users' understanding.  if
you don't care for consistent and correct use of terms (such as 'free
variable'), how can you expect users to understand your manuals, help
pages, and mailing list responses?

vQ
1 day later
#
Peter Dalgaard wrote:
an interesting point.  the two calls to gsubfn below should, in this
particular case, be equivalent:

library(gsubfn)

f = function(a) function(b) paste(a, b, sep="")
gsubfn('o', ~ f('o')(o), 'foo')
# "foooo"
gsubfn('o', ~ f(o)('o'), 'foo')
# the match seems to be ignored in the formula?

the following fails, too:

f = function(a) function() paste(a, a, sep="")
gsubfn('o', ~ f(o)(), 'foo')
# o won't capture the match

this as well, though it's rather different:

f = function() 'oo'
gsubfn('o', ~ f(), 'foo')
# really can't ignore the matched pattern if a formula is given?


while an average statistician may never write such rubbish code, these
are trivialized examples, and for a language advertised as one from the
functional family this sort of code is not so unusual and it may be
surprising that it fails.

vQ
#
This comes from the all.vars function and would indicate
a bug in that base R function.
character(0)


On Tue, Feb 3, 2009 at 8:24 AM, Wacek Kusnierczyk
<Waclaw.Marcin.Kusnierczyk at idi.ntnu.no> wrote:
Can you clarify this.  In what way was the "match ignored"?
In the first case it added an o after each o.  Were you expecting
something different?
#
Gabor Grothendieck wrote:
hush!  a user bug, i presume?  but indeed,

all.vars(expression(foo(bar)()))
# character(0)
all.names(expression(foo(bar)()))
# "foo" "bar"

vQ
#
Wacek Kusnierczyk wrote:
Semantic quibble!

Notice also that (same thing)

 > all.vars(~ fo(o)(),functions=T)
[1] "~"  "fo" "o"

The quibble is that functions=FALSE (default) can mean

(a) do not descend recursively into the function part (first element) of 
a call
(b) do descend, unless it is a name

what it does is clearly (a), but arguably, (b) is what the documentation 
says. This can be resolved in two ways...

Are there "legitimate" reasons to want behaviour (b)? That is, examples 
that would reasonably arise in practice.
#
Peter Dalgaard wrote:
if it is a name, how would you descend?  shouldn't the envisaged rule be
like:

when examining an operator expression,
(a) descend if it is a compound expression
(b) skip if it is a name

then 'foo(bar)()' would decompose to:

[('foo(bar)', '()']
[['foo', 'bar'], []] # by descent
=> ['bar'] # skip 'foo'

where square brackets denote parse tree (first two lines) and the
resulting list of names (last line).  'foo' skipped as being a simple
name in an operator position.

not sure about '~', i guess this is just an operator in the example
above, so it's actually

`~`(, foo(bar) ())

and the rule applies similarly.
one legitimate reason is to keep the syntax/semantics clean (worship the
god of boring pedantry). 
be this not enough, a practical example could certainly be found, though
admittedly the above were made up for the discussion. 

vQ
#
Wacek Kusnierczyk wrote:

            
By calling a recursive function which has it as the argument. It's not a 
problem unless you want it to be (first you descend into the first 
element on a call, then realize that it is a name). There are 
essentially three possibilities (cutting some red tape):

 > f <- function(e) if (is.name(e))
   print(e) else if(is.call(e)) invisible(lapply(e,f))
 > f(~ fo(o)())
`~`
fo
o


 > f <- function(e) if (is.name(e))
  print(e) else if(is.call(e)) invisible(lapply(e[-1],f))
 > f(~ fo(o)())


 > f <- function(e) if (is.name(e)) print(e) else
         if(is.call(e)) invisible( if(is.name(e[[1]]))
              lapply(e[-1],f) else lapply(e,f))
 > f(~ fo(o)())
o


The first two are essentially the current all.names and all.vars. The 
third is the one that you seem to expect. Notice that it gets rather 
more complicated than the others.

  > one legitimate reason is to keep the syntax/semantics clean (worship the
But can you be sure that there is no legitimate reason for expecting the 
current behaviour?
#
Peter Dalgaard wrote:
i was half-joking, and none of the below descends on a name.
yes.  'o' is certainly a free variable here, and not one in the operator
position.  which stilld does not mean that it cannot name a function,
but that's a different story, the issue is syntactical here.

the third form above is more complicated in implementation, but not in
use.  the interface is just the same.  sometimes it's better to
complicate the implementation to make the interface better (here:
performing an operation in accordance with the syntax of the language).
you surely know the answer.  virtually any feature in r i pointed out as
weird is claimed to be intentional, and thus there is a legitimate
reason (if only the intention itself) for it.  that's why i cannot be
sure this one is not intentional.  that's why it's possible that the
behaviour of gsubfn discussed before is a user bug (user = implementor
of gsubfn).

and of course i can't be sure that there isn't code in some package that
relies on the current behaviour of all.vars.  but if, as you said, this
is not what the documentation says, it should be no obstacle.  gsubfn,
for example, was caught by surprise, and does not seem to demand the
current state of the matters.

is this a design feature?  i guess not, and if it is an implementational
incident, it's not something that should necessarily persist.  perhaps
it's a good idea to do some design thinking first, and then reimplement
the stuff as designed (and documented).  perhaps as yet another all.*
function, or via an additional argument to all.vars.

vQ
#
Wacek Kusnierczyk wrote:

            
Actually, I don't. I was just pointing out the generic risk of fixing 
something that isn't broken by breaking something that works. There's a 
lot of conservatism in R where things don't get rationalized for similar 
reasons. For quite a while, bug-for-bug compatibility with S-PLUS v 3.x 
was considered important to allow people to port their packages between 
systems.