Tutorial on quosures

I've written up a tutorial on the basics of using quosures to make a simple and clean format, i.e. a DSL, for function arguments.

Thanks to @mara for suggestions to clarifiy some of the explanations in it.

You can find it here:

http://r.danal.com/tutorials/QuosuresByExamples.html

Case is important, sorry :smile:

10 Likes

Awesome. Really helped my understanding of what's going on "under the bonnet".

1 Like

Really great tutorial! Very clear and helps understand. To be shared to understand the quosure part of tidyevaluation.

@danr can we find the source somewhere to PR some typos corrections?

Thanks,

2 Likes

Hi danr,

Just as a heads up, I wouldn't rely too much on functions that we haven't advertised such as lang_tail(). The rlang API is still maturing and as a matter of fact the lang_ functions are likely to be revamped as call_. We'll be working on the lifecycle package to make it easier to figure out which parts of a maturing package are stable. Also we'll soon create a tidyeval package that will provide a stable entry point for the tidy eval functions.

Thanks for the heads up. lang_tail/head are exported by rlang... how can I tell which functions are advertised?

Will the lifecycle have similar replacement functions to help tease apart quosures?

The lifecycle package will be a set of conventions and tools that will serve the purpose of distinguishing experimental, maturing, stable and retired parts of a package. For now you can look at https://github.com/tidyverse/dplyr/blob/master/R/utils-tidy-eval.R to find what are the exported tidy eval functions.

+1 for the Seinfeld quote :joy:

Not to bug you but it doesn't seem like the functions exported utils-tidy-eval are sufficient to use quosures to make a DSL, even trivial ones like my example. Are you saying that at some point functions like lang_head and friends , or equivalent, will appear as functions with a call_ prefix or in some other form?

Also some functions like UQS and UQE are not exported by utils-tidy-eval but have been mentioned on community.rstudio.org by @nick and @hadley for use with quosures.

Also lang_head/tail are mentioned in the http://rlang.tidyverse.org/reference/lang.html document which itself is indirectly referenced by utility-tidy-eval.R via link it contains to documents about tidy eval.

I know a lot of the tidy eval stuff is in flux but I still would like to get my head around it.

Thanks,
Dan

1 Like

Are you saying that at some point functions like lang_head and friends , or equivalent, will appear as functions with a call_ prefix or in some other form?

Yes, using lang instead of call was meant to be consistent with the way R labels object types (see typeof(quote(foo()))) because we didn't want to create yet another nomenclature (cf historical mode vs type). However Hadley found it much easier to talk about calls rather than language object while writing the new edition of his book. As "call" is much more intuitive we are probably going to backtrack on this.

About lang_head() and lang_args() I had completely forgotten about these functions :). It's interesting that you find them useful to get your head around tidy eval and R expressions!

Note that you can use base subsetting to manipulate calls:

x <- quo(foo(bar, baz))
x
#> <quosure>
#> ~foo(bar, baz)

x[[2]]
#> foo(bar, baz)

x[[2]][[3]] <- quote(BAZ)
x
#> <quosure>
#> ~foo(bar, BAZ)

x[[2]][c(2, 3)] <- list(quote(qux), quote(quux))
#> <quosure>
#> ~foo(qux, quux)

You can also use this to insert quosures. However for cases where it makes sense we are advising a quasiquotation approach to manipulating calls. More on this in the next edition of adv-r!

2 Likes

Thanks Lionel! That's a much cleaner way to navigate an AST.

BTW I'm new to R but I've built parsers and even implemented processor instruction sets in the past... I'm trying to map what I've done in the past to how things work in R with whatever functions I happen to stumble over first :grinning:.

I wonder if quosures need their own methods to directly subset the wrapped expression. E.g. in the examples above you could omit the first subset level:

x[c(2, 3] <- list(quote(qux), quote(quux))

What do you think @hadley?

Edit: https://github.com/tidyverse/rlang/issues/292