> On 7 Dec 2020, at 17:35 , Duncan Murdoch <murdoch.duncan at gmail.com> wrote: > > On 07/12/2020 11:18 a.m., peter dalgaard wrote: >> Hmm, >> I feel a bit bad coming late to this, but I think I am beginning to side with those who want "... |> head" to work. And yes, that has to happen at the expense of |> head(). > > Just curious, how would you express head(df, 10)? Currently it is > > df |> head(10) > > Would I have to write it as > > df |> function(d) head(d, 10)It could be df |> ~ head(_, 10) which in a sense is "yes" to your question.> >> As I think it was Gabor points out, the current structure goes down a nonstandard evaluation route, which may be difficult to explain and departs from usual operator evaluation paradigms by being an odd mix of syntax and semantics. R lets you do these sorts of thing, witness ggplot and tidyverse, but the transparency of the language tends to suffer. > > I wouldn't call it non-standard evaluation. There is no function corresponding to |>, so there's no evaluation at all. It is more like the way "x -> y" is parsed as "y <- x", or "if (x) y" is transformed to `if`(x, y).That's a point, but maybe also my point. Currently, the parser is inserting the LHS as the 1st argument of the RHS, right? Things might be simpler if it was more like a simple binop. -pd> Duncan Murdoch > >> It would be neater if it was simply so that the class/type of the object on the right hand side decided what should happen. So we could have a rule that we could have an object, an expression, and possibly an unevaluated call on the RHS. Or maybe a formula, I.e., we could hav >> ... |> head >> but not >> ... |> head() >> because head() does not evaluate to anything useful. Instead, we could have some of these >> ... |> quote(head()) >> ... |> expression(head()) >> ... |> ~ head() >> ... |> \(_) head(_) >> possibly also using a placeholder mechanism for the three first ones. I kind of like the idea that the ~ could be equivalent to \(_). >> (And yes, I am kicking myself a bit for not using ~ in the NSE arguments in subset() and transform()) >> -pd >>> On 7 Dec 2020, at 16:20 , Deepayan Sarkar <deepayan.sarkar at gmail.com> wrote: >>> >>> On Mon, Dec 7, 2020 at 6:53 PM Gabor Grothendieck >>> <ggrothendieck at gmail.com> wrote: >>>> >>>> On Mon, Dec 7, 2020 at 5:41 AM Duncan Murdoch <murdoch.duncan at gmail.com> wrote: >>>>> I agree it's all about call expressions, but they aren't all being >>>>> treated equally: >>>>> >>>>> x |> f(...) >>>>> >>>>> expands to f(x, ...), while >>>>> >>>>> x |> `function`(...) >>>>> >>>>> expands to `function`(...)(x). This is an exception to the rule for >>>>> other calls, but I think it's a justified one. >>>> >>>> This admitted inconsistency is justified by what? No argument has been >>>> presented. The justification seems to be implicitly driven by implementation >>>> concerns at the expense of usability and language consistency. >>> >>> Sorry if I have missed something, but is your consistency argument >>> basically that if >>> >>> foo <- function(x) x + 1 >>> >>> then >>> >>> x |> foo >>> x |> function(x) x + 1 >>> >>> should both work the same? Suppose it did. Would you then be OK if >>> >>> x |> foo() >>> >>> no longer worked as it does now, and produced foo()(x) instead of foo(x)? >>> >>> If you are not OK with that and want to retain the current behaviour, >>> what would you want to happen with the following? >>> >>> bar <- function(x) function(n) rnorm(n, mean = x) >>> >>> 10 |> bar(runif(1))() # works 'as expected' ~ bar(runif(1))(10) >>> 10 |> bar(runif(1)) # currently bar(10, runif(1)) >>> >>> both of which you probably want. But then >>> >>> baz <- bar(runif(1)) >>> 10 |> baz >>> >>> (not currently allowed) will not be the same as what you would want from >>> >>> 10 |> bar(runif(1)) >>> >>> which leads to a different kind of inconsistency, doesn't it? >>> >>> -Deepayan >>> >>> ______________________________________________ >>> R-devel at r-project.org mailing list >>> https://stat.ethz.ch/mailman/listinfo/r-devel >-- Peter Dalgaard, Professor, Center for Statistics, Copenhagen Business School Solbjerg Plads 3, 2000 Frederiksberg, Denmark Phone: (+45)38153501 Office: A 4.23 Email: pd.mes at cbs.dk Priv: PDalgd at gmail.com
On 07/12/2020 12:09 p.m., Peter Dalgaard wrote:> > >> On 7 Dec 2020, at 17:35 , Duncan Murdoch <murdoch.duncan at gmail.com> wrote: >> >> On 07/12/2020 11:18 a.m., peter dalgaard wrote: >>> Hmm, >>> I feel a bit bad coming late to this, but I think I am beginning to side with those who want "... |> head" to work. And yes, that has to happen at the expense of |> head(). >> >> Just curious, how would you express head(df, 10)? Currently it is >> >> df |> head(10) >> >> Would I have to write it as >> >> df |> function(d) head(d, 10) > > It could be > > df |> ~ head(_, 10) > > which in a sense is "yes" to your question.I think that's doing too much weird stuff. I wouldn't want to have to teach it to beginners, whereas I think I could teach "df |> head(10)". That's doing one weird thing, but I'd count about three things I'd consider weird in yours.> >> >>> As I think it was Gabor points out, the current structure goes down a nonstandard evaluation route, which may be difficult to explain and departs from usual operator evaluation paradigms by being an odd mix of syntax and semantics. R lets you do these sorts of thing, witness ggplot and tidyverse, but the transparency of the language tends to suffer. >> >> I wouldn't call it non-standard evaluation. There is no function corresponding to |>, so there's no evaluation at all. It is more like the way "x -> y" is parsed as "y <- x", or "if (x) y" is transformed to `if`(x, y). > > That's a point, but maybe also my point. Currently, the parser is inserting the LHS as the 1st argument of the RHS, right? Things might be simpler if it was more like a simple binop.An advantage of the current implementation is that it's simple and easy to understand. Once you make it a user-modifiable binary operator, things will go kind of nuts. For example, I doubt if there are many users of magrittr's pipe who really understand its subtleties, e.g. the example in Luke's paper where 1 %>% c(., 2) gives c(1,2), but 1 %>% c(c(.), 2) gives c(1, 1, 2). (And I could add 1 %>% c(c(.), 2, .) and 1 %>% c(c(.), 2, . + 2) to continue the fun.) Duncan Murdoch> > -pd > >> Duncan Murdoch >> >>> It would be neater if it was simply so that the class/type of the object on the right hand side decided what should happen. So we could have a rule that we could have an object, an expression, and possibly an unevaluated call on the RHS. Or maybe a formula, I.e., we could hav >>> ... |> head >>> but not >>> ... |> head() >>> because head() does not evaluate to anything useful. Instead, we could have some of these >>> ... |> quote(head()) >>> ... |> expression(head()) >>> ... |> ~ head() >>> ... |> \(_) head(_) >>> possibly also using a placeholder mechanism for the three first ones. I kind of like the idea that the ~ could be equivalent to \(_). >>> (And yes, I am kicking myself a bit for not using ~ in the NSE arguments in subset() and transform()) >>> -pd >>>> On 7 Dec 2020, at 16:20 , Deepayan Sarkar <deepayan.sarkar at gmail.com> wrote: >>>> >>>> On Mon, Dec 7, 2020 at 6:53 PM Gabor Grothendieck >>>> <ggrothendieck at gmail.com> wrote: >>>>> >>>>> On Mon, Dec 7, 2020 at 5:41 AM Duncan Murdoch <murdoch.duncan at gmail.com> wrote: >>>>>> I agree it's all about call expressions, but they aren't all being >>>>>> treated equally: >>>>>> >>>>>> x |> f(...) >>>>>> >>>>>> expands to f(x, ...), while >>>>>> >>>>>> x |> `function`(...) >>>>>> >>>>>> expands to `function`(...)(x). This is an exception to the rule for >>>>>> other calls, but I think it's a justified one. >>>>> >>>>> This admitted inconsistency is justified by what? No argument has been >>>>> presented. The justification seems to be implicitly driven by implementation >>>>> concerns at the expense of usability and language consistency. >>>> >>>> Sorry if I have missed something, but is your consistency argument >>>> basically that if >>>> >>>> foo <- function(x) x + 1 >>>> >>>> then >>>> >>>> x |> foo >>>> x |> function(x) x + 1 >>>> >>>> should both work the same? Suppose it did. Would you then be OK if >>>> >>>> x |> foo() >>>> >>>> no longer worked as it does now, and produced foo()(x) instead of foo(x)? >>>> >>>> If you are not OK with that and want to retain the current behaviour, >>>> what would you want to happen with the following? >>>> >>>> bar <- function(x) function(n) rnorm(n, mean = x) >>>> >>>> 10 |> bar(runif(1))() # works 'as expected' ~ bar(runif(1))(10) >>>> 10 |> bar(runif(1)) # currently bar(10, runif(1)) >>>> >>>> both of which you probably want. But then >>>> >>>> baz <- bar(runif(1)) >>>> 10 |> baz >>>> >>>> (not currently allowed) will not be the same as what you would want from >>>> >>>> 10 |> bar(runif(1)) >>>> >>>> which leads to a different kind of inconsistency, doesn't it? >>>> >>>> -Deepayan >>>> >>>> ______________________________________________ >>>> R-devel at r-project.org mailing list >>>> https://stat.ethz.ch/mailman/listinfo/r-devel >> >
iuke-tier@ey m@iii@g oii uiow@@edu
2020-Dec-07 18:01 UTC
[Rd] [External] Re: New pipe operator
On Mon, 7 Dec 2020, Peter Dalgaard wrote:> > >> On 7 Dec 2020, at 17:35 , Duncan Murdoch <murdoch.duncan at gmail.com> wrote: >> >> On 07/12/2020 11:18 a.m., peter dalgaard wrote: >>> Hmm, >>> I feel a bit bad coming late to this, but I think I am beginning to side with those who want "... |> head" to work. And yes, that has to happen at the expense of |> head(). >> >> Just curious, how would you express head(df, 10)? Currently it is >> >> df |> head(10) >> >> Would I have to write it as >> >> df |> function(d) head(d, 10) > > It could be > > df |> ~ head(_, 10) > > which in a sense is "yes" to your question. > >> >>> As I think it was Gabor points out, the current structure goes down a nonstandard evaluation route, which may be difficult to explain and departs from usual operator evaluation paradigms by being an odd mix of syntax and semantics. R lets you do these sorts of thing, witness ggplot and tidyverse, but the transparency of the language tends to suffer. >> >> I wouldn't call it non-standard evaluation. There is no function corresponding to |>, so there's no evaluation at all. It is more like the way "x -> y" is parsed as "y <- x", or "if (x) y" is transformed to `if`(x, y). > > That's a point, but maybe also my point. Currently, the parser is inserting the LHS as the 1st argument of the RHS, right? Things might be simpler if it was more like a simple binop.It can only be a simple binop if you only allow RHS functions of one argument. Which would require currying along the lines Duncan showed. Something like: `%>>%` <- function(x, f) f(x) C1 <- function(f, ...) function(x) f(x, ...) mtcars %>>% head mtcars %>>% C1(head, 2) mtcars %>>% C1(subset, cyl == 4) %>>% \(d) lm(mpg ~ disp, data = d) This might fly if we lived in a world where most RHS functions take one argument and only a few needed currying. That is the case in many functional languages, but not for R. Making the common case of multiple arguments easy means you have to work at the source level, either in the parser or with some form of NSE. Best, luke> > -pd > >> Duncan Murdoch >> >>> It would be neater if it was simply so that the class/type of the object on the right hand side decided what should happen. So we could have a rule that we could have an object, an expression, and possibly an unevaluated call on the RHS. Or maybe a formula, I.e., we could hav >>> ... |> head >>> but not >>> ... |> head() >>> because head() does not evaluate to anything useful. Instead, we could have some of these >>> ... |> quote(head()) >>> ... |> expression(head()) >>> ... |> ~ head() >>> ... |> \(_) head(_) >>> possibly also using a placeholder mechanism for the three first ones. I kind of like the idea that the ~ could be equivalent to \(_). >>> (And yes, I am kicking myself a bit for not using ~ in the NSE arguments in subset() and transform()) >>> -pd >>>> On 7 Dec 2020, at 16:20 , Deepayan Sarkar <deepayan.sarkar at gmail.com> wrote: >>>> >>>> On Mon, Dec 7, 2020 at 6:53 PM Gabor Grothendieck >>>> <ggrothendieck at gmail.com> wrote: >>>>> >>>>> On Mon, Dec 7, 2020 at 5:41 AM Duncan Murdoch <murdoch.duncan at gmail.com> wrote: >>>>>> I agree it's all about call expressions, but they aren't all being >>>>>> treated equally: >>>>>> >>>>>> x |> f(...) >>>>>> >>>>>> expands to f(x, ...), while >>>>>> >>>>>> x |> `function`(...) >>>>>> >>>>>> expands to `function`(...)(x). This is an exception to the rule for >>>>>> other calls, but I think it's a justified one. >>>>> >>>>> This admitted inconsistency is justified by what? No argument has been >>>>> presented. The justification seems to be implicitly driven by implementation >>>>> concerns at the expense of usability and language consistency. >>>> >>>> Sorry if I have missed something, but is your consistency argument >>>> basically that if >>>> >>>> foo <- function(x) x + 1 >>>> >>>> then >>>> >>>> x |> foo >>>> x |> function(x) x + 1 >>>> >>>> should both work the same? Suppose it did. Would you then be OK if >>>> >>>> x |> foo() >>>> >>>> no longer worked as it does now, and produced foo()(x) instead of foo(x)? >>>> >>>> If you are not OK with that and want to retain the current behaviour, >>>> what would you want to happen with the following? >>>> >>>> bar <- function(x) function(n) rnorm(n, mean = x) >>>> >>>> 10 |> bar(runif(1))() # works 'as expected' ~ bar(runif(1))(10) >>>> 10 |> bar(runif(1)) # currently bar(10, runif(1)) >>>> >>>> both of which you probably want. But then >>>> >>>> baz <- bar(runif(1)) >>>> 10 |> baz >>>> >>>> (not currently allowed) will not be the same as what you would want from >>>> >>>> 10 |> bar(runif(1)) >>>> >>>> which leads to a different kind of inconsistency, doesn't it? >>>> >>>> -Deepayan >>>> >>>> ______________________________________________ >>>> R-devel at r-project.org mailing list >>>> https://stat.ethz.ch/mailman/listinfo/r-devel >> > >-- Luke Tierney Ralph E. Wareham Professor of Mathematical Sciences University of Iowa Phone: 319-335-3386 Department of Statistics and Fax: 319-335-3017 Actuarial Science 241 Schaeffer Hall email: luke-tierney at uiowa.edu Iowa City, IA 52242 WWW: http://www.stat.uiowa.edu