Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Syntax to support for expression enclosed by parentheses #38

Closed
renkun-ken opened this issue Aug 29, 2014 · 14 comments
Closed

Syntax to support for expression enclosed by parentheses #38

renkun-ken opened this issue Aug 29, 2014 · 14 comments
Milestone

Comments

@renkun-ken
Copy link
Owner

# element extracting
x %>>% (m)                  # x[["m"]]

# expression evaluation
x %>>% (fun(.))             # fun(x)
x %>>% (p ~ fun(p))         # fun(x)

# evaluation + assignment
x %>>% (fun(.) ~ y)         # y <- fun(x)    #### NOT TO SUPPORT
x %>>% (m ~ fun(m) ~ y)     # y <- fun(x)    #### NOT TO SUPPORT

# side effect
x %>>% (~ y)                # y <- x
x %>>% (~ fun(.))           # fun(x); x

# side effect: assignment
x %>>% (~ y)                # y <- x
x %>>% (~ fun(.) ~ y)       # y <- fun(x); x
x %>>% (~ m ~ fun(m) ~ y)   # y <- fun(x); x

# question (also side effect)
x %>>% (? fun(.))           # print(fun(x)); x
@renkun-ken
Copy link
Owner Author

The following syntax can be somewhat non-obvious or redundant.

x %>>% (fun(.) ~ y)  

## can be
x %>>% (f(.)) %>>% (~ y)

x %>>% (m ~ fun(m) ~ y)
# can be
x %>>% (m ~ fun(m)) %>>% (~y)

It is better to avoid any side-effect when evaluating enclosed expression within () that does not start with ~ or ?.

@yanlinlin82
Copy link

since

x %>>% (m) # x[["m"]]

why not misunderstand like this

x %>>% (fun(.) ~ y) # x[["y"]] <- fun(x)

On Sat, Aug 30, 2014 at 6:21 PM, Kun Ren notifications@github.com wrote:

The following syntax can be somewhat non-obvious or redundant.

x %>>% (fun(.) ~ y)

can be

x %>>% (f(.)) %>>% (~ y)

x %>>% (m ~ fun(m) ~ y)

can be

x %>>% (m ~ fun(m)) %>>% (~y)

It is better to avoid any side-effect when evaluating enclosed expression
within () that does not start with ~ or ?.


Reply to this email directly or view it on GitHub
#38 (comment).

@renkun-ken
Copy link
Owner Author

Sorry, the first comment is updated by the second one, and I propose that lambda expressions like

x %>>% (fun(.) ~ y)

should NOT be supported to perform action that produces side-effect (assignment).

I think it's best to restrict all syntax that involves side-effect to start with (~ as a one-sided formula to indicate side effect.

Clearly x[["y"]] <- fun(x) is a side effect that changes the value of lhs value.

Therefore, to be consistent, I suggest the following design principle:

ALL expressions in () that does not start with ~ should not involve side-effect feature.

As a result, one who takes a look at the code should be easy to find out mainstream pipeline and distinguish side-effects from it.

@yanlinlin82
Copy link

Sorry if I have missed something important.

Why use lambda expression rather than a real assignment like this:
x %>>% (fun(.) -> y)
x %>>% (y <- fun(.))
x %>>% (y = fun(.))

or, to make side-effect:
x %>>% (~ fun(.) -> y)
x %>>% (~ y <- fun(.))
x %>>% (~ y = fun(.))

which I thought should be more explicit as an assignment.

@renkun-ken
Copy link
Owner Author

The = syntax looks good :)

For the -> or <- syntax, it will not easily allow real assignment operations like x %>>% (env$x <- 2) and has conflict with currently deprecated syntax for ->. In fact, all -> will be parsed as <-. I would suggest not to support such syntax with <-.

For x %>>% (y = fun(.)), this looks good and clear, but will not straightly allow (~ symbol) that assigns lhs value to symbol. For function evaluation and assignment, I will try to see if it has some parsing issues.

Thanks for your suggestion!

@yanlinlin82
Copy link

My pleasure!

One more thing should be concerned carefully is that when the variable is
the same as the lhs:

x %>>% (x = fun(.))

Or more complex:

x %>>% (x = fun1(.)) %>>% (x = fun2(.))

Will the latter value overwrite the previous value? Will they affect the
subsequent pipe? Or some warnings/errors could be prompted?

@renkun-ken
Copy link
Owner Author

@yanlinlin82, I've just tried = syntax and it works perfectly so far :) and solves a problem I'm currently stuck at.

I tried

x %>>% (x = fun(.))

and it overwrite x for sure. For non-standard evaluation, all evaluation is done in the calling environment so the rewriting is quite obvious. Not sure yet whether to warn about overwriting.

@renkun-ken
Copy link
Owner Author

Now that the syntax

x %>>% (z = expr)

is equivalent to

z <- x %>>% (expr)

which means it supports element extracting, lambda expression, etc.

@yanlinlin82
Copy link

How about this case:

x <- data.frame(x = 1:3, y = 4:6)
x %>>% (~ x) %>>% print # will this be "assigning to `x`", "assigning to `x` column"
                        # or "extracting `x` column"?
                        # and what will be printed, value before or after change?

@renkun-ken
Copy link
Owner Author

The current implementation at branch feature/assign interprets x %>>% (~ x) as assigning x to x, then returns the original x then print it.

Therefore, it is assigning to x, then printing the value of the first x.

@renkun-ken
Copy link
Owner Author

This may look more consistent:

x %>>% (~ z) %>>% mean
# z <- x
# mean(x)

x %>>% (~ z = length(.)) %>>% mean
# z <- length(x)
# mean(x)

x %>>% (z = length(.)) %>>% mean
# z <- length(x)
# mean(z)

(~ always means side effect, this can be assignment, or arbitrary expression, whose value will be ignored and the input value is always returned.

Does this look ambiguous?

mtcars %>>%
  (~ lm_mtcars = lm(mpg ~ ., data = .)) %>>%
  subset(mpg <= mean(mpg)) %>>%
  (~ sub_mtcars) %>>%
  (lm_sub_mtcars = lm(mpg ~ ., data = .)) %>>%
  summary %>>%
  (coefficients) %>>%
  (~ coefs_mtcars)

@renkun-ken
Copy link
Owner Author

If x %>>% (y) and x %>>% (~ y) are put together, it may look confusing sometimes because they mean totally different things.

@yanlinlin82
Copy link

All right. I guess I could distinguish it now. Let me summarize it as:

  1. For x %>>% (EXP), it evaluates EXP by searching in object x
    first (for its column/element, btw. will it also work for S3/S4
    objects, picking the member?), then other environments , and finally
    return the value to subsequent pipe.
  2. For x %>>% (~ EXP), it evaluates EXP in the exact same way but
    pass the origin input value to subsequent pipe, aka "side effect",
    except that when EXP is a single symbol, which turns the meaning
    into assignment, because picking value from a variable as "side
    effect" makes no sense.

In the second case, could EXP be an expression that returns a
symbol? What will that mean?

@renkun-ken
Copy link
Owner Author

The rule is currently very definite and can be described as follows:

x %>>% (symbol)   # where is.symbol(symbol) = TRUE
# getElement(x,"symbol") which supports everything having [[ ]] and S4 objects.

x %>>% (call) # where is.call(call) = TRUE
# see if call is a formula (~) or equals (=) or otherwise

# 1. formula (~)
x %>>% (~ symbol) # symbol <- x; x
x %>>% (~ call) # eval call with . = x as side effect and returns x
x %>>% (symbol ~ call) # eval call with symbol = x
x %>>% (~symbol ~ call) # eval call with symbol = x as side effect and returns x

# 2. equals (=)
x %>>% (y = expr) # y <- x %>>% (expr)
x %>>% (~ y = expr) # y <- x %>>% (expr) as side effect and returns x

# 3. otherwise
x %>>% (call) # pipe to .

Therefore, your first point should be if x %>>% (EXP) where EXP is a symbol, it only extracts elements EXP from x and that's all, no further trial to find the element in calling environment. EXP should be exactly an element of x. I carefully thought of this design before and believe it can reduce the risks of ambiguity. I'll try to explain this later.

The second point is all right. For the question, here is an example:

1:10 %>>% (~ quote(x))

where quote(x) returns a symbol x. This expression should not be evaluated here I think, it looks like a meta-meta language object made on purpose.

@renkun-ken renkun-ken added this to the 0.4-3 milestone Sep 12, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants