On 12/6/20 4:32 PM, Duncan Murdoch wrote:
> On 06/12/2020 9:43 a.m., D?nes T?th wrote:
>> Dear Luke,
>>
>> In the meantime I checked the R-syntax branch and the docs; they are
>> very helpful. I would also like to thank you for putting effort into
>> this feature. Keeping it at the syntax level is also a very smart
>> decision. However, the current API might not exploit the full power of
>> the basic idea.
>>
>> 1) Requiring either an anonymous function or a function call, but not
>> allowing for symbols which point to functions is inconsistent and will
>> be misleading for non-experts.
>>
>> foo <- function(x) x
>> identical(foo, function(x) x)
>>
>> mtcars |> foo #bang!
>> mtcars |> function(x) x #fine?
>
> You are missing the point. The value of the RHS is irrelevant to the
> transformation. All that matters is its form. So "foo" and
> "function(x) x" are completely different things, even if
identical()
> thinks their value is the same.
We are at the syntax level, so of course we do not know the value of the
RHS when the parsing occurs. I *do* understand that the *form* is
important here, but how do you explain this to a rookie R user? He will
see that he entered two expressions which he thinks are identical, even
though they are not identical at the level when the parsing occurs.
Also think of the potential users of this syntax. There are at least two
groups:
1) ~95% of the users: active users of `%>%`. My experience is that the
vast majority of them do not use the "advanced" features of magrittr;
however, they are got used to things like mtcars |> print. Provide them
with the RHS-as-symbol syntax and they will be happy - they have a
plug-and-forget replacement. Or do enforce a function call - they will
be unhappy, and will not adopt the new syntax.
2) ~5% of the users (including me): have not used magrittr or any other
(probably better) implementations (e.g., pipeR, wrapr) of the pipe
operator because it could lead to nasty performance issues, bugs, and
debugging problems. However, from the functional-programming-style of
view, these users might prefer the new syntax and as few typing as
possible.
>
> It's also true that "foo()" and "function(x) x"
are completely
> different, but they are well-defined forms: one is a call, the other is
> an anonymous function definition.
>
> Accepting a plain "foo" would add a third form (a name), which
might
> make sense, but hardly gains anything:
I would reverse the argumentation: Luke has a working implementation for
the case if the RHS is a single symbol. What do we loose if we keep it?
Best,
Denes
> whereas dropping the anonymous
> function definition costs quite a bit. Without special-casing anonymous
> function definitions you'd need to enter
>
> mtcars |> (function(x) x)()
>
> or
>
> mtcars |> (\(x) x)()
>
> which are both quite difficult to read.
>
> Duncan Murdoch
>
>>
>> You stated in :
>> "
>> Another variation supported by the implementation is that a symbol on
>> the RHS is interpreted as the name of a function to call with the LHS
>> as argument:
>>
>> ```r
>> > quote(x |> f)
>> f(x)
>> ```
>> "
>>
>> So clearly this is not an implementation issue but a design decision.
>>
>> As a remedy, two different pipe operators could be introduced:
>>
>> LHS |> RHS -> RHS is treated as a function call
>> LHS |>> RHS -> RHS is treated as a function
>>
>> If |>> is used, it would not matter which notation is used for
the RHS
>> expression; the parser would assume it evaluates to a function.
>>
>> 2) Simplified lambda expression:
>> IMHO in the vast majority of use cases, this is used for
single-argument
>> functions, so parenthesis would not be required. Hence, both forms
would
>> be valid and equivalent:
>>
>> \x x + 1
>> \(x) x + 1
>>
>>
>> 3) Function composition:
>> Allowing for concise composition of functions would be a great
feature.
>> E.g., instead of
>>
>> foo <- function(x) print(mean(sqrt(x), na.rm = TRUE), digits = 2)
>>
>> or
>>
>> foo <- \x {x |> sqrt() |> mean(na.rm = TRUE) |>
print(digits = 2)}
>>
>> one could write
>>
>> foo <- \x |> sqrt() |> mean(na.rm = TRUE) |> print(digits
= 2)
>>
>> So basically if the lambda argument is followed by a pipe operator,
the
>> pipe chain is transformed to a function body where the first lambda
>> argument is inserted into the first position of the pipeline.
>>
>>
>> Best,
>> Denes
>>
>>
>> On 12/5/20 7:10 PM, luke-tierney at uiowa.edu wrote:
>>> We went back and forth on this several times. The key advantage of
>>> requiring parentheses is to keep things simple and consistent.
Let's
>>> get some experience with that. If experience shows requiring
>>> parentheses creates too many issues then we can add the option of
>>> dropping them later (with special handling of :: and :::).
It's easier
>>> to add flexibility and complexity than to restrict it after the
fact.
>>>
>>> Best,
>>>
>>> luke
>>>
>>> On Sat, 5 Dec 2020, Hugh Parsonage wrote:
>>>
>>>> I'm surprised by the aversion to
>>>>
>>>> mtcars |> nrow
>>>>
>>>> over
>>>>
>>>> mtcars |> nrow()
>>>>
>>>> and I think the decision to disallow the former should be
>>>> reconsidered. The pipe operator is only going to be used when
the rhs
>>>> is a function, so there is no ambiguity with omitting the
parentheses.
>>>> If it's disallowed, it becomes inconsistent with other
treatments like
>>>> sapply(mtcars, typeof) where sapply(mtcars, typeof()) would
just be
>>>> noise. I'm not sure why this decision was taken
>>>>
>>>> If the only issue is with the double (and triple) colon
operator, then
>>>> ideally `mtcars |> base::head` should resolve to
`base::head(mtcars)`
>>>> -- in other words, demote the precedence of |>
>>>>
>>>> Obviously (looking at the R-Syntax branch) this decision was
>>>> considered, put into place, then dropped, but I can't see
why
>>>> precisely.
>>>>
>>>> Best,
>>>>
>>>>
>>>> Hugh.
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> On Sat, 5 Dec 2020 at 04:07, Deepayan Sarkar
>>>> <deepayan.sarkar at gmail.com> wrote:
>>>>>
>>>>> On Fri, Dec 4, 2020 at 7:35 PM Duncan Murdoch
>>>>> <murdoch.duncan at gmail.com> wrote:
>>>>>>
>>>>>> On 04/12/2020 8:13 a.m., Hiroaki Yutani wrote:
>>>>>>>> Error: function '::' not supported
in RHS call of a pipe
>>>>>>>
>>>>>>> To me, this error looks much more friendly than
magrittr's error.
>>>>>>> Some of them got too used to specify functions
without (). This
>>>>>>> is OK until they use `::`, but when they need to
use it, it takes
>>>>>>> hours to figure out why
>>>>>>>
>>>>>>> mtcars %>% base::head
>>>>>>> #> Error in .::base : unused argument (head)
>>>>>>>
>>>>>>> won't work but
>>>>>>>
>>>>>>> mtcars %>% head
>>>>>>>
>>>>>>> works. I think this is a too harsh lesson for
ordinary R users to
>>>>>>> learn `::` is a function. I've been wanting
for magrittr to
drop the
>>>>>>> support for a function name without () to avoid
this confusion,
>>>>>>> so I would very much welcome the new pipe
operator's behavior.
>>>>>>> Thank you all the developers who implemented this!
>>>>>>
>>>>>> I agree, it's an improvement on the corresponding
magrittr error.
>>>>>>
>>>>>> I think the semantics of not evaluating the RHS, but
treating the
>>>>>> pipe
>>>>>> as purely syntactical is a good decision.
>>>>>>
>>>>>> I'm not sure I like the recommended way to pipe
into a particular
>>>>>> argument:
>>>>>>
>>>>>> mtcars |> subset(cyl == 4) |> \(d) lm(mpg ~
disp, data = d)
>>>>>>
>>>>>> or
>>>>>>
>>>>>> mtcars |> subset(cyl == 4) |> function(d)
lm(mpg ~ disp, data
>>>>>> = d)
>>>>>>
>>>>>> both of which are equivalent to
>>>>>>
>>>>>> mtcars |> subset(cyl == 4) |> (function(d)
lm(mpg ~ disp, data >>>>>> d))()
>>>>>>
>>>>>> It's tempting to suggest it should allow something
like
>>>>>>
>>>>>> mtcars |> subset(cyl == 4) |> lm(mpg ~ disp,
data = .)
>>>>>
>>>>> Which is really not that far off from
>>>>>
>>>>> mtcars |> subset(cyl == 4) |> \(.) lm(mpg ~ disp,
data = .)
>>>>>
>>>>> once you get used to it.
>>>>>
>>>>> One consequence of the implementation is that it's not
clear how
>>>>> multiple occurrences of the placeholder would be
interpreted. With
>>>>> magrittr,
>>>>>
>>>>> sort(runif(10)) %>% ecdf(.)(.)
>>>>> ## [1] 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
>>>>>
>>>>> This is probably what you would expect, if you expect it
to work at
>>>>> all, and not
>>>>>
>>>>> ecdf(sort(runif(10)))(sort(runif(10)))
>>>>>
>>>>> There would be no such ambiguity with anonymous functions
>>>>>
>>>>> sort(runif(10)) |> \(.) ecdf(.)(.)
>>>>>
>>>>> -Deepayan
>>>>>
>>>>>> which would be expanded to something equivalent to the
other
>>>>>> versions:
>>>>>> but that makes it quite a bit more complicated.
(Maybe _ or \.
>>>>>> should
>>>>>> be used instead of ., since those are not legal
variable names.)
>>>>>>
>>>>>> I don't think there should be an attempt to copy
magrittr's special
>>>>>> casing of how . is used in determining whether to also
include the
>>>>>> previous value as first argument.
>>>>>>
>>>>>> Duncan Murdoch
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> Best,
>>>>>>> Hiroaki Yutani
>>>>>>>
>>>>>>> 2020?12?4?(?) 20:51 Duncan Murdoch
<murdoch.duncan at gmail.com>:
>>>>>>>>
>>>>>>>> Just saw this on the R-devel news:
>>>>>>>>
>>>>>>>>
>>>>>>>> R now provides a simple native pipe syntax
?|>? as well as a
>>>>>>>> shorthand
>>>>>>>> notation for creating functions, e.g. ?\(x) x
+ 1? is parsed as
>>>>>>>> ?function(x) x + 1?. The pipe implementation
as a syntax
>>>>>>>> transformation
>>>>>>>> was motivated by suggestions from Jim Hester
and Lionel Henry.
>>>>>>>> These
>>>>>>>> features are experimental and may change prior
to release.
>>>>>>>>
>>>>>>>>
>>>>>>>> This is a good addition; by using
"|>" instead of "%>%" there
>>>>>>>> should be
>>>>>>>> a chance to get operator precedence right.
That said, the ?Syntax
>>>>>>>> help
>>>>>>>> topic hasn't been updated, so I'm not
sure where it fits in.
>>>>>>>>
>>>>>>>> There are some choices that take a little
getting used to:
>>>>>>>>
>>>>>>>> > mtcars |> head
>>>>>>>> Error: The pipe operator requires a function
call or an anonymous
>>>>>>>> function expression as RHS
>>>>>>>>
>>>>>>>> (I need to say mtcars |> head() instead.)
This sometimes leads to
>>>>>>>> error
>>>>>>>> messages that are somewhat confusing:
>>>>>>>>
>>>>>>>> > mtcars |> magrittr::debug_pipe |>
head
>>>>>>>> Error: function '::' not supported in
RHS call of a pipe
>>>>>>>>
>>>>>>>> but
>>>>>>>>
>>>>>>>> mtcars |> magrittr::debug_pipe() |>
head()
>>>>>>>>
>>>>>>>> works.
>>>>>>>>
>>>>>>>> Overall, I think this is a great addition,
though it's going to be
>>>>>>>> disruptive for a while.
>>>>>>>>
>>>>>>>> Duncan Murdoch
>>>>>>>>
>>>>>>>> ______________________________________________
>>>>>>>> R-devel at r-project.org mailing list
>>>>>>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>>>>>>
>>>>>>> ______________________________________________
>>>>>>> R-devel at r-project.org mailing list
>>>>>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>>>>>>
>>>>>>
>>>>>> ______________________________________________
>>>>>> R-devel at r-project.org mailing list
>>>>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>>>>
>>>>> ______________________________________________
>>>>> R-devel at r-project.org mailing list
>>>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>>>
>>>> ______________________________________________
>>>> R-devel at r-project.org mailing list
>>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>>>
>>>
>>
>> ______________________________________________
>> R-devel at r-project.org mailing list
>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>
>
>