I'm thrilled to hear it! Thank you!
- Tim
P.S. I re-added the r-devel list, since Kevin's reply was sent just to me,
but I thought there might be others interested in knowing about those work
items. (I hope that's OK, email-etiquette-wise.)
On Wed, Dec 9, 2020 at 1:10 PM Kevin Ushey <kevinushey at gmail.com>
wrote:
> You might be surprised to learn that the RStudio IDE engineers might
> be receptive to such a feature request. :-)
>
> https://github.com/rstudio/rstudio/issues/8589
> https://github.com/rstudio/rstudio/issues/8590
>
> (Spoiler alert: I am one of the RStudio IDE engineers, and I think
> this would be worth doing.)
>
> Best,
> Kevin
>
> On Wed, Dec 9, 2020 at 12:16 PM Timothy Goodman <timsgoodman at
gmail.com>
> wrote:
> >
> > Since my larger concern is being able to conveniently select and
re-run
> part of a multiline pipeline, I don't think wrapping in parentheses
will
> help. I'd have to add a closing paren at the end of the selection,
which
> is no more convenient than having to highlight all but the last pipe.
> (Admittedly, wrapping in parens would allow my preferred syntax of having
> pipes at the start of the line, but I don't think that's worth the
cost of
> having to constantly move the trailing paren around.)
> >
> > My back-up plan if I fail to persuade you all is indeed to beg the
> developers of RStudio to add an option to do the transformation I would
> want when executing notebook code, but I'm anticipating the objection
of "R
> Notebooks shouldn't transform invalid R code into valid R code."
I was
> hoping "Let's make this new pipe |> work differently in a case
that's
> currently an error" would be an easier sell.
> >
> > Also, just to reiterate: Only one of my two suggestions really
requires
> caring about newlines. (That's my preferred solution, but I understand
> it'd be the bigger change.) The other suggestion just amounts to
ignoring
> a final |> when code is submitted for execution.
> >
> > -Tim
> >
> > On Wed, Dec 9, 2020 at 11:58 AM Kevin Ushey <kevinushey at
gmail.com>
> wrote:
> >>
> >> I agree with Duncan that the right solution is to wrap the pipe
> >> expression with parentheses. Having the parser treat newlines
> >> differently based on whether the session is interactive, or on
what
> >> type of operator happens to follow a newline, feels like a pretty
big
> >> can of worms.
> >>
> >> I think this (or something similar) would accomplish what you want
> >> while still retaining the nice aesthetics of the pipe expression,
with
> >> a minimal amount of syntax "noise":
> >>
> >> result <- (
> >> data
> >> |> op1()
> >> |> op2()
> >> )
> >>
> >> For interactive sessions where you wanted to execute only parts of
the
> >> pipeline at a time, I could see that being accomplished by the
editor
> >> -- it could transform the expression so that it could be handled
by R,
> >> either by hoisting the pipe operator(s) up a line, or by wrapping
the
> >> to-be-executed expression in parentheses for you. If such a style
of
> >> coding became popular enough, I'm sure the developers of such
editors
> >> would be interested and willing to support this ...
> >>
> >> Perhaps more importantly, it would be much easier to accomplish
than a
> >> change to the behavior of the R parser, and it would be work that
> >> wouldn't have to be maintained by the R Core team.
> >>
> >> Best,
> >> Kevin
> >>
> >> On Wed, Dec 9, 2020 at 11:34 AM Timothy Goodman <timsgoodman at
gmail.com>
> wrote:
> >> >
> >> > If I type my_data_frame_1 and press Enter (or Ctrl+Enter to
execute
> the
> >> > command in the Notebook environment I'm using) I
certainly *would*
> expect R
> >> > to treat it as a complete statement.
> >> >
> >> > But what I'm talking about is a different case, where I
highlight a
> >> > multi-line statement in my notebook:
> >> >
> >> > my_data_frame1
> >> > |> filter(some_conditions_1)
> >> >
> >> > and then press Ctrl+Enter. Or, I suppose the equivalent
would be to
> run an
> >> > R script containing those two lines of code, or to run a
multi-line
> >> > statement like that from the console (which in RStudio I can
do by
> pressing
> >> > Shift+Enter between the lines.)
> >> >
> >> > In those cases, R could either (1) Give an error message [the
current
> >> > behavior], or (2) understand that the first line is meant to
be piped
> to
> >> > the second. The second option would be significantly more
useful,
> and is
> >> > almost certainly what the user intended.
> >> >
> >> > (For what it's worth, there are some languages, such as
Javascript,
> that
> >> > consider the first token of the next line when determining if
the
> previous
> >> > line was complete. JavaScript's rules around this are
overly
> complicated,
> >> > but a rule like "a pipe following a line break is
treated as
> continuing the
> >> > previous line" would be much simpler. And while it
might be
> objectionable
> >> > to treat the operator %>% different from other operators,
the
> addition of
> >> > |>, which isn't truly an operator at all, seems like
the right time to
> >> > consider it.)
> >> >
> >> > -Tim
> >> >
> >> > On Wed, Dec 9, 2020 at 3:12 AM Duncan Murdoch <
> murdoch.duncan at gmail.com>
> >> > wrote:
> >> >
> >> > > The requirement for operators at the end of the line
comes from the
> >> > > interactive nature of R. If you type
> >> > >
> >> > > my_data_frame_1
> >> > >
> >> > > how could R know that you are not done, and are planning
to type the
> >> > > rest of the expression
> >> > >
> >> > > %>% filter(some_conditions_1)
> >> > > ...
> >> > >
> >> > > before it should consider the expression complete? The
way
> languages
> >> > > like C do this is by requiring a statement terminator at
the end.
> You
> >> > > can also do it by wrapping the entire thing in
parentheses ().
> >> > >
> >> > > However, be careful: Don't use braces: they
don't work. And parens
> >> > > have the side effect of removing invisibility from the
result
> (which is
> >> > > a design flaw or bonus, depending on your point of
view). So I
> actually
> >> > > wouldn't advise this workaround.
> >> > >
> >> > > Duncan Murdoch
> >> > >
> >> > >
> >> > > On 09/12/2020 12:45 a.m., Timothy Goodman wrote:
> >> > > > Hi,
> >> > > >
> >> > > > I'm a data scientist who routinely uses R in my
day-to-day work,
> for
> >> > > tasks
> >> > > > such as cleaning and transforming data, exploratory
data
> analysis, etc.
> >> > > > This includes frequent use of the pipe operator
from the magrittr
> and
> >> > > dplyr
> >> > > > libraries, %>%. So, I was pleased to hear about
the recent work
> on a
> >> > > > native pipe operator, |>.
> >> > > >
> >> > > > This seems like a good time to bring up the main
pain point I
> encounter
> >> > > > when using pipes in R, and some suggestions on what
could be done
> about
> >> > > > it. The issue is that the pipe operator can't
be placed at the
> start of
> >> > > a
> >> > > > line of code (except in parentheses). That's
no different than
> any
> >> > > binary
> >> > > > operator in R, but I find it's a source of
difficulty for the pipe
> >> > > because
> >> > > > of how pipes are often used.
> >> > > >
> >> > > > [I'm assuming here that my usage is fairly
typical of a lot of
> users; at
> >> > > > any rate, I don't think I'm *too* unusual.]
> >> > > >
> >> > > > === Why this is a problem ==> >> > >
>
> >> > > > It's very common (for me, and I suspect for
many users of dplyr)
> to write
> >> > > > multi-step pipelines and put each step on its own
line for
> readability.
> >> > > > Something like this:
> >> > > >
> >> > > > ### Example 1 ###
> >> > > > my_data_frame_1 %>%
> >> > > > filter(some_conditions_1) %>%
> >> > > > inner_join(my_data_frame_2, by =
some_columns_1) %>%
> >> > > > group_by(some_columns_2) %>%
> >> > > > summarize(some_aggregate_functions_1) %>%
> >> > > > filter(some_conditions_2) %>%
> >> > > > left_join(my_data_frame_3, by =
some_columns_3) %>%
> >> > > > group_by(some_columns_4) %>%
> >> > > > summarize(some_aggregate_functions_2) %>%
> >> > > > arrange(some_columns_5)
> >> > > >
> >> > > > [I guess some might consider this an overly long
pipeline; for me
> it's
> >> > > > pretty typical. I *could* split it up by assigning
intermediate
> results
> >> > > to
> >> > > > variables, but much of the value I get from the
pipe is that it
> lets my
> >> > > > code communicate which results are temporary, and
which will be
> used
> >> > > again
> >> > > > later. Assigning variables for single-use results
would remove
> that
> >> > > > expressiveness.]
> >> > > >
> >> > > > I would prefer (for reasons I'll explain) to be
able to write the
> above
> >> > > > example like this, which isn't valid R:
> >> > > >
> >> > > > ### Example 2 (not valid R) ###
> >> > > > my_data_frame_1
> >> > > > %>% filter(some_conditions_1)
> >> > > > %>% inner_join(my_data_frame_2, by =
some_columns_1)
> >> > > > %>% group_by(some_columns_2)
> >> > > > %>% summarize(some_aggregate_functions_1)
> >> > > > %>% filter(some_conditions_2)
> >> > > > %>% left_join(my_data_frame_3, by =
some_columns_3)
> >> > > > %>% group_by(some_columns_4)
> >> > > > %>% summarize(some_aggregate_functions_2)
> >> > > > %>% arrange(some_columns_5)
> >> > > >
> >> > > > One (minor) advantage is obvious: It lets you
easily line up the
> pipes,
> >> > > > which means that you can see at a glance that the
whole block is
> a single
> >> > > > pipeline, and you'd immediately notice if you
inadvertently
> omitted a
> >> > > pipe,
> >> > > > which otherwise can lead to confusing output.
[It's also
> aesthetically
> >> > > > pleasing, especially when %>% is replaced with
|>, but that's
> >> > > subjective.]
> >> > > >
> >> > > > But the bigger issue happens when I want to re-run
just *part* of
> the
> >> > > > pipeline. I do this often when debugging: if the
output of the
> pipeline
> >> > > > seems wrong, I re-run the first few steps and check
the output,
> then
> >> > > > include a little more and re-run again, etc., until
I locate my
> mistake.
> >> > > > Working in an interactive notebook environment,
this involves
> using the
> >> > > > cursor to select just the part of the code I want
to re-run.
> >> > > >
> >> > > > It's fast and easy to select *entire* lines of
code, but
> unfortunately
> >> > > with
> >> > > > the pipes placed at the end of the line I must
instead select
> everything
> >> > > > *except* the last three characters of the line (the
last two
> characters
> >> > > for
> >> > > > the new pipe). Then when I want to re-run the same
partial
> pipeline with
> >> > > > the next line of code included, I can't just
press SHIFT+Down to
> select
> >> > > it
> >> > > > as I otherwise would, but instead must move the
cursor
> horizontally to a
> >> > > > position three characters before the end of *that*
line (which is
> >> > > generally
> >> > > > different due to varying line lengths). And so
forth each time I
> want to
> >> > > > include an additional line.
> >> > > >
> >> > > > Moreover, with the staggered positions of the pipes
at the end of
> each
> >> > > > line, it's very easy to accidentally select the
final pipe on a
> line, and
> >> > > > then sit there for a moment wondering if the
environment has
> stopped
> >> > > > responding before realizing it's just waiting
for further input
> (i.e.,
> >> > > for
> >> > > > the right-hand side). These small delays and
disruptions add up
> over the
> >> > > > course of a day.
> >> > > >
> >> > > > This desire to select and re-run the first part of
a pipeline is
> also the
> >> > > > reason why it doesn't suffice to achieve syntax
like my "Example
> 2" by
> >> > > > wrapping the entire pipeline in parentheses.
That's of no use if
> I want
> >> > > to
> >> > > > re-run a selection that doesn't include the
final close-paren.
> >> > > >
> >> > > > === Possible Solutions ==> >> > >
>
> >> > > > I can think of two, but maybe there are others.
The first would
> make
> >> > > > "Example 2" into valid code, and the
second would allow you to
> run a
> >> > > > selection that included a trailing pipe.
> >> > > >
> >> > > > Solution 1: Add a special case to how R is
parsed, so if the
> first
> >> > > > (non-whitespace) token after an end-line is a pipe,
that pipe
> gets moved
> >> > > to
> >> > > > before the end-line.
> >> > > > - Argument for: This lets you write code like
example 2,
> which
> >> > > > addresses the pain point around re-running part of
a pipeline,
> and has
> >> > > > advantages for readability. Also, since starting a
line with a
> pipe
> >> > > > operator is currently invalid, the change
wouldn't break any
> working
> >> > > code.
> >> > > > - Argument against: It would make the behavior
of %>%
> inconsistent
> >> > > with
> >> > > > that of other binary operators in R. (However,
this objection
> might not
> >> > > > apply to the new pipe, |>, which I understand is
being
> implemented as a
> >> > > > syntax transformation rather than a binary
operator.)
> >> > > >
> >> > > > Solution 2: Ignore the pipe operator if it
occurs as the final
> token
> >> > > of
> >> > > > the code being executed.
> >> > > > - Argument for: This would mean the user could
select and
> re-run the
> >> > > > first few lines of a longer pipeline (selecting
*entire* lines),
> avoiding
> >> > > > the difficulties described above.
> >> > > > - Argument against: This means that %>%
would be valid even
> if it
> >> > > > occurred without a right-hand side, which is
inconsistent with
> other
> >> > > > operators in R. (But, as above, this objection
might not apply
> to |>.)
> >> > > > Also, this solution still doesn't enable the
syntax of "Example
> 2", with
> >> > > > its readability benefit.
> >> > > >
> >> > > > Thanks for reading this and considering it.
> >> > > >
> >> > > > - Tim Goodman
> >> > > >
> >> > > > [[alternative HTML version deleted]]
> >> > > >
> >> > > > ______________________________________________
> >> > > > R-devel at r-project.org mailing list
> >> > > > https://stat.ethz.ch/mailman/listinfo/r-devel
> >> > > >
> >> > >
> >> > >
> >> >
> >> > [[alternative HTML version deleted]]
> >> >
> >> > ______________________________________________
> >> > R-devel at r-project.org mailing list
> >> > https://stat.ethz.ch/mailman/listinfo/r-devel
>
[[alternative HTML version deleted]]