[med-svn] [r-cran-lazyeval] 04/12: New upstream version 0.2.0

Andreas Tille tille at debian.org
Fri Nov 10 09:20:43 UTC 2017


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository r-cran-lazyeval.

commit 65dee5f8f06235358b168b201f49bb935d20f209
Author: Andreas Tille <tille at debian.org>
Date:   Fri Nov 10 09:59:51 2017 +0100

    New upstream version 0.2.0
---
 DESCRIPTION                      |  23 ++
 MD5                              |  82 ++++++
 NAMESPACE                        | 104 +++++++
 NEWS.md                          |  66 +++++
 R/ast.R                          |  87 ++++++
 R/call.R                         |  76 +++++
 R/complain.R                     |  45 +++
 R/expr.R                         |  96 ++++++
 R/f-capture.R                    |  35 +++
 R/f-eval.R                       | 103 +++++++
 R/f-interp.R                     |  76 +++++
 R/formula.R                      | 183 ++++++++++++
 R/function.R                     |  35 +++
 R/language.R                     | 117 ++++++++
 R/lazy-as.R                      | 109 +++++++
 R/lazy-call.R                    | 104 +++++++
 R/lazy-dots.R                    |  59 ++++
 R/lazy-eval.R                    |  31 ++
 R/lazy-interp.R                  | 100 +++++++
 R/lazy-names.R                   |  43 +++
 R/lazy.R                         |  68 +++++
 R/utils.R                        |  37 +++
 README.md                        |  21 ++
 build/vignette.rds               | Bin 0 -> 252 bytes
 debian/README.test               |   9 -
 debian/changelog                 |  13 -
 debian/compat                    |   1 -
 debian/control                   |  23 --
 debian/copyright                 |  28 --
 debian/docs                      |   3 -
 debian/rules                     |   4 -
 debian/source/format             |   1 -
 debian/tests/control             |   3 -
 debian/tests/run-unit-test       |  12 -
 debian/watch                     |   3 -
 inst/doc/lazyeval-old.R          | 107 +++++++
 inst/doc/lazyeval-old.Rmd        | 212 ++++++++++++++
 inst/doc/lazyeval-old.html       | 269 +++++++++++++++++
 inst/doc/lazyeval.R              | 319 ++++++++++++++++++++
 inst/doc/lazyeval.Rmd            | 617 +++++++++++++++++++++++++++++++++++++++
 inst/doc/lazyeval.html           | 606 ++++++++++++++++++++++++++++++++++++++
 man/all_dots.Rd                  |  24 ++
 man/as.lazy.Rd                   |  36 +++
 man/as_name.Rd                   |  28 ++
 man/ast_.Rd                      |  32 ++
 man/auto_name.Rd                 |  25 ++
 man/call_modify.Rd               |  41 +++
 man/call_new.Rd                  |  28 ++
 man/common_env.Rd                |  23 ++
 man/expr_label.Rd                |  59 ++++
 man/f_capture.Rd                 |  40 +++
 man/f_eval.Rd                    |  75 +++++
 man/f_interp.Rd                  |  60 ++++
 man/f_list.Rd                    |  29 ++
 man/f_new.Rd                     |  24 ++
 man/f_rhs.Rd                     |  50 ++++
 man/f_text.Rd                    |  38 +++
 man/f_unwrap.Rd                  |  21 ++
 man/function_new.Rd              |  40 +++
 man/interp.Rd                    |  47 +++
 man/is_formula.Rd                |  19 ++
 man/is_lang.Rd                   |  46 +++
 man/lazy_.Rd                     |  58 ++++
 man/lazy_dots.Rd                 |  47 +++
 man/lazy_eval.Rd                 |  31 ++
 man/make_call.Rd                 |  34 +++
 man/missing_arg.Rd               |  16 +
 src/expr.c                       |  76 +++++
 src/interp.c                     |  48 +++
 src/lazy.c                       | 107 +++++++
 src/name.c                       |  55 ++++
 src/utils.c                      |  74 +++++
 src/utils.h                      |  13 +
 tests/testthat.R                 |   4 +
 tests/testthat/ast-irregular.txt |   4 +
 tests/testthat/ast-sample.txt    |  25 ++
 tests/testthat/test-ast.R        |  27 ++
 tests/testthat/test-call.R       |  44 +++
 tests/testthat/test-complain.R   |  39 +++
 tests/testthat/test-dots.R       |  21 ++
 tests/testthat/test-expr.R       |  80 +++++
 tests/testthat/test-f-capture.R  |  28 ++
 tests/testthat/test-f-eval.R     |  83 ++++++
 tests/testthat/test-f-interp.R   |  68 +++++
 tests/testthat/test-f-list.R     |  51 ++++
 tests/testthat/test-f-unwrap.R   |  19 ++
 tests/testthat/test-formula.R    |  73 +++++
 tests/testthat/test-function.R   |  12 +
 tests/testthat/test-language.R   |  26 ++
 tests/testthat/test-lazy.R       |  47 +++
 tests/testthat/test-names.R      |   8 +
 vignettes/lazyeval-old.Rmd       | 212 ++++++++++++++
 vignettes/lazyeval.Rmd           | 617 +++++++++++++++++++++++++++++++++++++++
 vignettes/lazyeval.nb.html       | 335 +++++++++++++++++++++
 94 files changed, 6997 insertions(+), 100 deletions(-)

diff --git a/DESCRIPTION b/DESCRIPTION
new file mode 100644
index 0000000..201ab02
--- /dev/null
+++ b/DESCRIPTION
@@ -0,0 +1,23 @@
+Package: lazyeval
+Version: 0.2.0
+Title: Lazy (Non-Standard) Evaluation
+Description: An alternative approach to non-standard evaluation using
+    formulas. Provides a full implementation of LISP style 'quasiquotation',
+    making it easier to generate code with other code.
+Authors at R: c(
+    person("Hadley", "Wickham", ,"hadley at rstudio.com", c("aut", "cre")),
+    person("RStudio", role = "cph")
+    )
+License: GPL-3
+LazyData: true
+Depends: R (>= 3.1.0)
+Suggests: knitr, rmarkdown (>= 0.2.65), testthat, covr
+VignetteBuilder: knitr
+RoxygenNote: 5.0.1
+NeedsCompilation: yes
+Packaged: 2016-06-10 21:38:52 UTC; hadley
+Author: Hadley Wickham [aut, cre],
+  RStudio [cph]
+Maintainer: Hadley Wickham <hadley at rstudio.com>
+Repository: CRAN
+Date/Publication: 2016-06-12 19:03:08
diff --git a/MD5 b/MD5
new file mode 100644
index 0000000..a20c62b
--- /dev/null
+++ b/MD5
@@ -0,0 +1,82 @@
+fa9cd2a9bbc39b62c157b6314731acd1 *DESCRIPTION
+7b2947f5d0d0271183df72fca2ce3e3d *NAMESPACE
+cf66555e902bf658a46f21098c4b3924 *NEWS.md
+d9cc209f4c0c733a11ed93e673ae0d9c *R/ast.R
+befb1c81988ed1357c416f919528980d *R/call.R
+e3159cd804827c6d85885c17b284b5fb *R/complain.R
+77caaa0ea35cd37de5223d4e196a3ae8 *R/expr.R
+50c6c7b9a0ed29d6a0339267382cbc0b *R/f-capture.R
+c1e8a4f2908cc985b4faed2c04bebc7f *R/f-eval.R
+937485929e7484b04ba928363c40783f *R/f-interp.R
+6af036082308c708e8c41c2feddfc342 *R/formula.R
+ea161e248423ef0d17885123c1065258 *R/function.R
+be4330310f7efddd7fefe5052db9a8e0 *R/language.R
+d9a423351a93e1514bfac54255d4eb28 *R/lazy-as.R
+f2b5984a77262840aea4bf29785ed9b9 *R/lazy-call.R
+657685967b8807536a446cb3b4e066e1 *R/lazy-dots.R
+66bfb57dbc11072d7581559830ba1994 *R/lazy-eval.R
+41ed7c6a852958828eb44d08374bff84 *R/lazy-interp.R
+3cbae872f5df31e044ddf79731550d93 *R/lazy-names.R
+1fb72726cd71726fd765f99b9201721d *R/lazy.R
+4501ecd06bc9f3d7513cc558cc195440 *R/utils.R
+20befb8e739c2bb4a0f0b3127094c034 *README.md
+d81073433d99ffc4634fac4854ce8738 *build/vignette.rds
+01e2dd08598e3e44bab0fb7a03a40d6c *inst/doc/lazyeval-old.R
+fd0e6bc6fa61549d20cc69f641dd2ffd *inst/doc/lazyeval-old.Rmd
+eb784bcdb14416449e1f444429aad13d *inst/doc/lazyeval-old.html
+5f895ae6e564ecd4855e5ae17b1e1216 *inst/doc/lazyeval.R
+036dd3ea1f555238004da3c88ffb2373 *inst/doc/lazyeval.Rmd
+10e7eaef568acaa72b0096c27dc303ce *inst/doc/lazyeval.html
+03e28452c0b2edfa94ed1082c8368ffd *man/all_dots.Rd
+c3709c4449a6bbda09c1517b1f5af844 *man/as.lazy.Rd
+9ab475b8e753f516261951deda3fa016 *man/as_name.Rd
+f0d6875f5040038fcf5c8c8a28f753ac *man/ast_.Rd
+fe5e4aaee2e4bf7e6c33e8f4c0325f47 *man/auto_name.Rd
+7065bdcd616febd822fd627fb5dfc80b *man/call_modify.Rd
+3adf4392696f87f3fe20be863e097030 *man/call_new.Rd
+cac4ff895c5a07de960880458d93e25c *man/common_env.Rd
+4c398d8819d35ab0ddfb33ec4362ccb5 *man/expr_label.Rd
+1adeb570aa4c952f60c2877a3e837765 *man/f_capture.Rd
+7de2d26c479c618917953141fe61b71a *man/f_eval.Rd
+52db51794b9bfd4bfd63e882a9580760 *man/f_interp.Rd
+86188063b7691e5ba7555efdc22b2ced *man/f_list.Rd
+ff05de5c1ca041f7c637b07e832e98ca *man/f_new.Rd
+d5bb30d0072508cc7df571477745646d *man/f_rhs.Rd
+fcfa4b57c381a89662600b1535f6bbf5 *man/f_text.Rd
+6006c0d80c0d8024f6079ee9ff6e387b *man/f_unwrap.Rd
+546aca85c89f0f07b635afab74dc6e93 *man/function_new.Rd
+6f3839e1f92d109ae2325a5377d04dca *man/interp.Rd
+0966ab35dae62a21e2725893d7db865c *man/is_formula.Rd
+20860876d74853d5434de1195ad5d1dd *man/is_lang.Rd
+47bdb7f92aa7939a613275d02cd33a5d *man/lazy_.Rd
+1699f8204d5ad958d59b44c089bb6605 *man/lazy_dots.Rd
+79e602e1583f70cd93abeb737506555f *man/lazy_eval.Rd
+911496ab1d254d0b1995552ad9b2bd12 *man/make_call.Rd
+d5dd6510243621a9e1db205e800c337c *man/missing_arg.Rd
+7b905d827832f25ae40fef01890066e9 *src/expr.c
+e4155ab688f6e7c66de0d688ece032ac *src/interp.c
+d7a002f57621390d47008a7a48f50034 *src/lazy.c
+c903c7364ad618995a6d13798a60695d *src/name.c
+ea674e2342add37360bafee9e0fbcbc2 *src/utils.c
+26ca4f69c34debf19d0665cc61dcea80 *src/utils.h
+b43a93ea47dc4f6a64a4625fb67b87f6 *tests/testthat.R
+28ab6d5c95cce52309b007a83e869cd1 *tests/testthat/ast-irregular.txt
+eda899de62882fc656bfbd548171ee04 *tests/testthat/ast-sample.txt
+eaca1b22c21c0bd595aa7be2b8de7c6c *tests/testthat/test-ast.R
+2a7ae04e75bf6ca648d1d8b7f0f0f255 *tests/testthat/test-call.R
+bd8c6bcb7b287fa288af9d8dd003c46c *tests/testthat/test-complain.R
+c62da31fc3eed1f72d5e9926d90d5e3e *tests/testthat/test-dots.R
+c31c22fe7c0f3ab3723dcf15b574e365 *tests/testthat/test-expr.R
+03a6e2c3ec4675634e9d46be2794138d *tests/testthat/test-f-capture.R
+943b81371b45f3ab141370765197be09 *tests/testthat/test-f-eval.R
+b5ea255aacfa712ab108dff7bc031543 *tests/testthat/test-f-interp.R
+f13ad194342bbc3166d97bae4d2c0f0a *tests/testthat/test-f-list.R
+11bfa6279aa0e9589ea9d4f1b6d62a6e *tests/testthat/test-f-unwrap.R
+ab37f7090d977e24438cd1ff79e371af *tests/testthat/test-formula.R
+cf2bc1d7126c6275bb6a66200c86cdc4 *tests/testthat/test-function.R
+4e12b82cde56263135ae960627dce9cf *tests/testthat/test-language.R
+7c37c5cf3fab082e9dfda9942e2f72e0 *tests/testthat/test-lazy.R
+dcd46b347706159f3b53165d13ee9272 *tests/testthat/test-names.R
+fd0e6bc6fa61549d20cc69f641dd2ffd *vignettes/lazyeval-old.Rmd
+036dd3ea1f555238004da3c88ffb2373 *vignettes/lazyeval.Rmd
+98671c6e82620ce1a9e731aeaa2bc674 *vignettes/lazyeval.nb.html
diff --git a/NAMESPACE b/NAMESPACE
new file mode 100644
index 0000000..d1c9971
--- /dev/null
+++ b/NAMESPACE
@@ -0,0 +1,104 @@
+# Generated by roxygen2: do not edit by hand
+
+S3method("$",complain)
+S3method("$<-",lazy_dots)
+S3method("[",lazy_dots)
+S3method("[<-",lazy_dots)
+S3method("[[",complain)
+S3method(as.lazy,call)
+S3method(as.lazy,character)
+S3method(as.lazy,formula)
+S3method(as.lazy,lazy)
+S3method(as.lazy,logical)
+S3method(as.lazy,name)
+S3method(as.lazy,numeric)
+S3method(as.lazy_dots,"NULL")
+S3method(as.lazy_dots,call)
+S3method(as.lazy_dots,character)
+S3method(as.lazy_dots,formula)
+S3method(as.lazy_dots,lazy)
+S3method(as.lazy_dots,lazy_dots)
+S3method(as.lazy_dots,list)
+S3method(as.lazy_dots,name)
+S3method(as_call,call)
+S3method(as_call,character)
+S3method(as_call,formula)
+S3method(as_call,name)
+S3method(as_name,call)
+S3method(as_name,character)
+S3method(as_name,formula)
+S3method(as_name,name)
+S3method(c,lazy_dots)
+S3method(find_data,"NULL")
+S3method(find_data,data.frame)
+S3method(find_data,default)
+S3method(find_data,list)
+S3method(has_name,default)
+S3method(has_name,environment)
+S3method(interp,call)
+S3method(interp,character)
+S3method(interp,formula)
+S3method(interp,lazy)
+S3method(interp,name)
+S3method(print,lazy)
+export("f_env<-")
+export("f_lhs<-")
+export("f_rhs<-")
+export(all_dots)
+export(as.lazy)
+export(as.lazy_dots)
+export(as_call)
+export(as_f_list)
+export(as_name)
+export(ast)
+export(ast_)
+export(auto_name)
+export(call_modify)
+export(call_new)
+export(call_standardise)
+export(common_env)
+export(dots_capture)
+export(expr_env)
+export(expr_find)
+export(expr_label)
+export(expr_text)
+export(f_capture)
+export(f_env)
+export(f_eval)
+export(f_eval_lhs)
+export(f_eval_rhs)
+export(f_interp)
+export(f_label)
+export(f_lhs)
+export(f_list)
+export(f_new)
+export(f_rhs)
+export(f_text)
+export(f_unwrap)
+export(find_data)
+export(function_new)
+export(interp)
+export(is_atomic)
+export(is_call)
+export(is_formula)
+export(is_lang)
+export(is_name)
+export(is_pairlist)
+export(lazy)
+export(lazy_)
+export(lazy_dots)
+export(lazy_eval)
+export(make_call)
+export(missing_arg)
+export(uq)
+export(uqf)
+export(uqs)
+useDynLib(lazyeval,env)
+useDynLib(lazyeval,expr_env_)
+useDynLib(lazyeval,expr_find_)
+useDynLib(lazyeval,interp_)
+useDynLib(lazyeval,lhs)
+useDynLib(lazyeval,lhs_name)
+useDynLib(lazyeval,make_lazy)
+useDynLib(lazyeval,make_lazy_dots)
+useDynLib(lazyeval,rhs)
diff --git a/NEWS.md b/NEWS.md
new file mode 100644
index 0000000..0553190
--- /dev/null
+++ b/NEWS.md
@@ -0,0 +1,66 @@
+# lazyeval 0.2.0
+
+## Formula-based lazy evaluation
+
+Lazyeval has a  new system for lazy-eval based on formulas, described in depth in the new `lazyeval` vignette. This system is still a little experimental - it hasn't seen much use outside of the vignette, so it certainly may change a little in the future. However, long-term goal is to use these tools across all of my packages (ggplot2, tidyr, dplyr, etc), and I am fairly confident that this is a robust system that won't need major changes.
+
+There are three key components:
+
+* `f_eval()` evaluates a formula in the environment where it was defined. 
+  If supplied, values are first looked for in an optional `data` argument. 
+  Pronouns `.data` and `.env` can be used to resolve ambiguity in this case.
+  (#43). Longer forms `f_eval_rhs()` and `f_eval_lhs()` emphasise the side
+  of the formula that you want to evaluate (#64).
+  
+* `f_interp()` provides a full quasiquoting system using `uq()` for unquote
+  and `uqs()` for unquote-splice (#36).
+
+* `f_capture()` and `dots_capture()` make it easy to turn promises
+  and `...` into explicit formulas. These should be used sparingly, as
+  generally lazy-eval is preferred to non-standard eval.
+  
+* For functions that work with `...`, `f_list()` and `as_f_list()` make it
+  possible to use the evaluated LHS of a formula to name the elements of a 
+  list (#59).
+
+The core components are accompanied by a number of helper functions:
+
+* Identify a formula with `is_formula()`.
+
+* Create a formula from a quoted call and an environment with `f_new()`.
+
+* "Unwrap" a formula removing one level from the stack of parent environments 
+  with `f_unwrap()`.
+  
+* Get or set either side of a formula with `f_rhs()` or `f_lhs()`, and
+  the environment with `f_env()`.
+  
+* Convert to text/label with `f_text()` and `f_label()`.
+
+I've also added `expr_find()`, `expr_text()` and `expr_label()` explicitly to find the expression associated with a function argument, and label it for output (#58). This is one of the primary uses cases for NSE. `expr_env()` is a similar helper that returns the environment associated with a promise (#67).
+
+## Fixes to existing functions
+
+* `lazy_dots()` gains `.ignore_empty` argument to drop extra arguments (#32).
+
+* `interp.formula()` only accepts single-sided formulas (#37).
+
+* `interp()` accepts an environment in `.values` (#35).
+
+* `interp.character()` always produes a single string, regardless of
+  input length (#27).
+
+* Fixed an infinite loop in `lazy_dots(.follow_symbols = TRUE)` (#22, #24)
+
+* `lazy()` now fails with an informative error when it is applied on
+  an object that has already been evaluated (#23, @lionel-).
+
+* `lazy()` no longer follows the expressions of lazily loaded objects
+  (#18, @lionel-).
+
+# lazyeval 0.1.10
+
+* `as.lazy_dots()` gains a method for NULL, returning a zero-length
+  list.
+
+* `auto_names()` no longer truncates symbols (#19, #20)
diff --git a/R/ast.R b/R/ast.R
new file mode 100644
index 0000000..492b29d
--- /dev/null
+++ b/R/ast.R
@@ -0,0 +1,87 @@
+#' Display a call (or expression) as a tree.
+#'
+#' \code{ast_} takes a quoted expression; \code{ast} does the quoting
+#' for you.
+#'
+#' @param x Quoted call, list of calls, or expression to display.
+#' @param width Display width, defaults to current width as reported by
+#'   \code{getOption("width")}.
+#' @export
+#' @examples
+#' ast(f(x, 1, g(), h(i())))
+#' ast(if (TRUE) 3 else 4)
+#' ast(function(a = 1, b = 2) {a + b + 10})
+#' ast(f(x)(y)(z))
+#'
+#' ast_(quote(f(x, 1, g(), h(i()))))
+#' ast_(quote(if (TRUE) 3 else 4))
+#' ast_(expression(1, 2, 3))
+ast_ <- function(x, width = getOption("width")) {
+  if (is.expression(x) || is.list(x)) {
+    trees <- vapply(x, tree, character(1), width = width)
+    out <- paste0(trees, collapse = "\n\n")
+  } else {
+    out <- tree(x, width = width)
+  }
+
+  cat(out, "\n")
+}
+
+#' @rdname ast_
+#' @export
+ast <- function(x) ast_(expr_find(x))
+
+tree <- function(x, level = 1, width = getOption("width"), branch = "\u2517 ") {
+  if (is_atomic(x) && length(x) == 1) {
+    label <- paste0(" ", deparse(x)[1])
+    children <- NULL
+  } else if (is_name(x)) {
+    x <- as.character(x)
+    if (x == "") {
+      # Special case the missing argument
+      label <- "`MISSING"
+    } else {
+      label <- paste0("`", as.character(x))
+    }
+
+    children <- NULL
+  } else if (is_call(x)) {
+    label <- "()"
+    children <-  vapply(as.list(x), tree, character(1),
+      level = level + 1, width = width - 3)
+  } else if (is_pairlist(x)) {
+    label <- "[]"
+
+    branches <- paste("\u2517", format(names(x)), "=")
+    children <- character(length(x))
+    for (i in seq_along(x)) {
+      children[i] <- tree(x[[i]], level = level + 1, width = width - 3,
+        branch = branches[i])
+    }
+  } else {
+    # Special case for srcrefs, since they're commonly seen
+    if (inherits(x, "srcref")) {
+      label <- "<srcref>"
+    } else {
+      label <- paste0("<", typeof(x), ">")
+    }
+    children <- NULL
+  }
+
+  indent <- paste0(str_dup(" ", level - 1), branch)
+  label <- str_trunc(label, width - 3)
+
+  if (is.null(children)) {
+    paste0(indent, label)
+  } else {
+    paste0(indent, label, "\n", paste0(children, collapse = "\n"))
+  }
+}
+
+str_trunc <- function(x, width = getOption("width")) {
+  ifelse(nchar(x) <= width, x, paste0(substr(x, 1, width - 3), "..."))
+}
+
+str_dup <- function(x, n) {
+  paste0(rep(x, n), collapse = "")
+}
diff --git a/R/call.R b/R/call.R
new file mode 100644
index 0000000..d27a07b
--- /dev/null
+++ b/R/call.R
@@ -0,0 +1,76 @@
+#' Create a call by "hand"
+#'
+#' @param f Function to call. For \code{make_call}, either a string, a symbol
+#'   or a quoted call. For \code{do_call}, a bare function name or call.
+#' @param ...,.args Arguments to the call either in or out of a list
+#' @export
+#' @examples
+#' # f can either be a string, a symbol or a call
+#' call_new("f", a = 1)
+#' call_new(quote(f), a = 1)
+#' call_new(quote(f()), a = 1)
+#'
+#' #' Can supply arguments individually or in a list
+#' call_new(quote(f), a = 1, b = 2)
+#' call_new(quote(f), .args = list(a = 1, b = 2))
+call_new <- function(f, ..., .args = list()) {
+  if (is.character(f)) {
+    if (length(f) != 1) {
+      stop("Character `f` must be length 1", call. = FALSE)
+    }
+    f <- as.name(f)
+  }
+
+  args <- c(list(...), as.list(.args))
+  as.call(c(f, args))
+}
+
+#' Modify the arguments of a call.
+#'
+#' @param call A call to modify. It is first standardised with
+#'   \code{\link{call_standardise}}.
+#' @param env Environment in which to look up call value.
+#' @param new_args A named list of expressions (constants, names or calls)
+#'   used to modify the call. Use \code{NULL} to remove arguments.
+#' @export
+#' @examples
+#' call <- quote(mean(x, na.rm = TRUE))
+#' call_standardise(call)
+#'
+#' # Modify an existing argument
+#' call_modify(call, list(na.rm = FALSE))
+#' call_modify(call, list(x = quote(y)))
+#'
+#' # Remove an argument
+#' call_modify(call, list(na.rm = NULL))
+#'
+#' # Add a new argument
+#' call_modify(call, list(trim = 0.1))
+#'
+#' # Add an explicit missing argument
+#' call_modify(call, list(na.rm = quote(expr = )))
+call_modify <- function(call, new_args, env = parent.frame()) {
+  stopifnot(is.call(call), is.list(new_args))
+
+  call <- call_standardise(call, env)
+
+  if (!all(has_names(new_args))) {
+    stop("All new arguments must be named", call. = FALSE)
+  }
+
+  for (nm in names(new_args)) {
+    call[[nm]] <- new_args[[nm]]
+  }
+  call
+}
+
+#' @rdname call_modify
+#' @export
+call_standardise <- function(call, env = parent.frame()) {
+  stopifnot(is_call(call))
+
+  f <- eval(call[[1]], env)
+  if (is.primitive(f)) return(call)
+
+  match.call(f, call)
+}
diff --git a/R/complain.R b/R/complain.R
new file mode 100644
index 0000000..165114b
--- /dev/null
+++ b/R/complain.R
@@ -0,0 +1,45 @@
+complain <- function(x, message = "object '%s' not found") {
+  if (is.null(x)) {
+    return(NULL)
+  }
+
+  if (is.environment(x)) {
+    x <- clone_env(x)
+  }
+
+  structure(x, message = message, class = c("complain", class(x)))
+}
+
+clone_env <- function(x) {
+  list2env(as.list(x, all.names = TRUE), parent = parent.env(x))
+}
+
+#' @export
+`$.complain` <- function(x, name) {
+  if (!has_name(x, name)) {
+    stop(sprintf(attr(x, "message"), name), call. = FALSE)
+  }
+  x[[name]]
+}
+
+#' @export
+`[[.complain` <- function(x, i, ...) {
+  if (!is.character(i) || length(i) != 1) {
+    stop("Must subset with a string", call. = FALSE)
+  }
+  if (!has_name(x, i)) {
+    stop(sprintf(attr(x, "message"), i), call. = FALSE)
+  }
+  NextMethod()
+}
+has_name <- function(x, name) {
+  UseMethod("has_name")
+}
+#' @export
+has_name.default <- function(x, name) {
+  name %in% names(x)
+}
+#' @export
+has_name.environment <- function(x, name) {
+  exists(name, envir = x, inherits = FALSE)
+}
diff --git a/R/expr.R b/R/expr.R
new file mode 100644
index 0000000..427ee7f
--- /dev/null
+++ b/R/expr.R
@@ -0,0 +1,96 @@
+#' Find the expression associated with an argument
+#'
+#' \code{expr_find()} finds the full expression; \code{expr_text()} turns the
+#' expression into a single string; \code{expr_label()} formats it nicely for
+#' use in messages. \code{expr_env()} finds the environment associated with
+#' the expression.
+#'
+#' These functions never force promises, and will work even if a promise has
+#' previously been forced.
+#'
+#' @param x A promise (function argument)
+#' @export
+#' @examples
+#' # Unlike substitute(), expr_find() finds the original expression
+#' f <- function(x) g(x)
+#' g <- function(y) h(y)
+#' h <- function(z) list(substitute(z), expr_find(z))
+#'
+#' f(1 + 2 + 3)
+#'
+#' expr_label(10)
+#' # Names a quoted with ``
+#' expr_label(x)
+#' # Strings are encoded
+#' expr_label("a\nb")
+#' # Expressions are captured
+#' expr_label(a + b + c)
+#' # Long expressions are collapsed
+#' expr_label(foo({
+#'   1 + 2
+#'   print(x)
+#' }))
+expr_label <- function(x) {
+  expr_label_(expr_find(x))
+}
+
+expr_label_ <- function(x) {
+  if (is.character(x)) {
+    encodeString(x, quote = '"')
+  } else if (is.atomic(x)) {
+    format(x)
+  } else if (is.name(x)) {
+    paste0("`", as.character(x), "`")
+  } else {
+    chr <- deparse(x)
+    if (length(chr) > 1) {
+      dot_call <- call_new(x[[1]], quote(...))
+      chr <- paste(deparse(dot_call), collapse = "\n")
+    }
+    paste0("`", chr, "`")
+  }
+}
+
+#' @export
+#' @rdname expr_label
+#' @param width Width of each line
+#' @param nlines Maximum number of lines to extract.
+expr_text <- function(x, width = 60L, nlines = Inf) {
+  expr_text_(expr_find(x), width = width, nlines = nlines)
+}
+
+expr_text_ <- function(x, width = 60L, nlines = Inf) {
+  str <- deparse(x, width.cutoff = width)
+
+  if (length(str) > nlines) {
+    str <- c(str[seq_len(nlines - 1)], "...")
+  }
+
+  paste0(str, collapse = "\n")
+}
+
+#' @useDynLib lazyeval expr_find_
+#' @export
+#' @rdname expr_label
+expr_find <- function(x) {
+  .Call(expr_find_, quote(x), environment())
+}
+
+#' @useDynLib lazyeval expr_env_
+#' @param default_env If supplied, \code{expr_env} will return this if the
+#'   promise has already been forced. Otherwise it will throw an error.
+#' @export
+#' @rdname expr_label
+expr_env <- function(x, default_env) {
+  env <- .Call(expr_env_, quote(x), environment())
+
+  if (is.null(env)) {
+    if (missing(default_env)) {
+      stop("Promise has already been forced")
+    } else {
+      default_env
+    }
+  } else {
+    env
+  }
+}
diff --git a/R/f-capture.R b/R/f-capture.R
new file mode 100644
index 0000000..f278484
--- /dev/null
+++ b/R/f-capture.R
@@ -0,0 +1,35 @@
+#' Make a promise explicit by converting into a formula.
+#'
+#' This should be used sparingly if you want to implement true non-standard
+#' evaluation with 100\% magic. I recommend avoiding this unless you have
+#' strong reasons otherwise since requiring arguments to be formulas only
+#' adds one extra character to the inputs, and otherwise makes life much much
+#' simpler.
+#'
+#' @param x,... An unevaluated promises
+#' @param .ignore_empty If \code{TRUE}, empty arguments will be silently
+#'    dropped.
+#' @export
+#' @return \code{f_capture} returns a formula; \code{dots_capture}
+#'   returns a list of formulas.
+#' @examples
+#' f_capture(a + b)
+#' dots_capture(a + b, c + d, e + f)
+#'
+#' # These functions will follow a chain of promises back to the
+#' # original definition
+#' f <- function(x) g(x)
+#' g <- function(y) h(y)
+#' h <- function(z) f_capture(z)
+#' f(a + b + c)
+f_capture <- function(x) {
+  lazy <- .Call(make_lazy, quote(x), environment(), TRUE)
+  f_new(lazy$expr, env = lazy$env)
+}
+
+#' @export
+#' @rdname f_capture
+dots_capture <- function(..., .ignore_empty = TRUE) {
+  lazies <- .Call(make_lazy_dots, environment(), TRUE, .ignore_empty)
+  lapply(lazies, function(x) f_new(x$expr, env = x$env))
+}
diff --git a/R/f-eval.R b/R/f-eval.R
new file mode 100644
index 0000000..04952b8
--- /dev/null
+++ b/R/f-eval.R
@@ -0,0 +1,103 @@
+#' @export
+#' @rdname f_eval
+f_eval_rhs <- function(f, data = NULL) {
+  if (!is_formula(f)) {
+    stop("`f` is not a formula", call. = FALSE)
+  }
+
+  expr <- f_rhs(f_interp(f, data = data))
+  eval_expr(expr, f_env(f), data)
+}
+
+#' @export
+#' @rdname f_eval
+f_eval_lhs <- function(f, data = NULL) {
+  if (!is_formula(f)) {
+    stop("`f` is not a formula", call. = FALSE)
+  }
+
+  expr <- f_lhs(f_interp(f, data = data))
+  eval_expr(expr, f_env(f), data)
+}
+
+#' Evaluate a formula
+#'
+#' \code{f_eval_rhs} evaluates the RHS of a formula and \code{f_eval_lhs}
+#' evaluates the LHS. \code{f_eval} is a shortcut for \code{f_eval_rhs} since
+#' that is what you most commonly need.
+#'
+#' If \code{data} is specified, variables will be looked for first in this
+#' object, and if not found in the environment of the formula.
+#'
+#' @section Pronouns:
+#' When used with \code{data}, \code{f_eval} provides two pronouns to make it
+#' possible to be explicit about where you want values to come from:
+#' \code{.env} and \code{.data}. These are thin wrappers around \code{.data}
+#' and \code{.env} that throw errors if you try to access non-existent values.
+#'
+#' @param f A formula. Any expressions wrapped in \code{ uq() } will
+#'   will be "unquoted", i.e. they will be evaluated, and the results inserted
+#'   back into the formula. See \code{\link{f_interp}} for more details.
+#' @param data A list (or data frame). \code{find_data} is a generic used to
+#'   find the data associated with a given object. If you want to make
+#'   \code{f_eval} work for your own objects, you can define a method for this
+#'   generic.
+#' @param x An object for which you want to find associated data.
+#' @export
+#' @examples
+#' f_eval(~ 1 + 2 + 3)
+#'
+#' # formulas automatically capture their enclosing environment
+#' foo <- function(x) {
+#'   y <- 10
+#'   ~ x + y
+#' }
+#' f <- foo(1)
+#' f
+#' f_eval(f)
+#'
+#' # If you supply data, f_eval will look their first:
+#' f_eval(~ cyl, mtcars)
+#'
+#' # To avoid ambiguity, you can use .env and .data pronouns to be
+#' # explicit:
+#' cyl <- 10
+#' f_eval(~ .data$cyl, mtcars)
+#' f_eval(~ .env$cyl, mtcars)
+#'
+#' # Imagine you are computing the mean of a variable:
+#' f_eval(~ mean(cyl), mtcars)
+#' # How can you change the variable that's being computed?
+#' # The easiest way is "unquote" with uq()
+#' # See ?f_interp for more details
+#' var <- ~ cyl
+#' f_eval(~ mean( uq(var) ), mtcars)
+f_eval <- f_eval_rhs
+
+
+eval_expr <- function(expr, env, data) {
+  data <- find_data(data)
+
+  expr_env <- new.env(parent = env)
+  expr_env$.env <- complain(env, "Object '%s' not found in environment")
+  expr_env$.data <- complain(data, "Variable '%s' not found in data")
+
+  eval(expr, data, expr_env)
+}
+
+
+#' @rdname f_eval
+#' @export
+find_data <- function(x) UseMethod("find_data")
+
+#' @export
+find_data.NULL <- function(x) list()
+#' @export
+find_data.list <- function(x) x
+#' @export
+find_data.data.frame <- function(x) x
+
+#' @export
+find_data.default <- function(x) {
+  stop("Do not know how to find data associated with `x`", call. = FALSE)
+}
diff --git a/R/f-interp.R b/R/f-interp.R
new file mode 100644
index 0000000..bc5c753
--- /dev/null
+++ b/R/f-interp.R
@@ -0,0 +1,76 @@
+#' Interpolate a formula
+#'
+#' Interpolation replaces sub-expressions of the form \code{uq(x)} with
+#' the evaluated value of \code{x}, and inlines sub-expressions of
+#' the form \code{uqs(x)}.
+#'
+#' @section Theory:
+#' Formally, \code{f_interp} is a quasiquote function, \code{uq()} is the
+#' unquote operator, and \code{uqs()} is the unquote splice operator.
+#' These terms have a rich history in LISP, and live on in modern languages
+#' like \href{Julia}{http://docs.julialang.org/en/release-0.1/manual/metaprogramming/}
+#' and \href{Racket}{https://docs.racket-lang.org/reference/quasiquote.html}.
+#'
+#' @param f A one-sided formula.
+#' @param x For \code{uq} and \code{uqf}, a formula. For \code{uqs}, a
+#'   a vector.
+#' @param data When called from inside \code{f_eval}, this is used to pass on
+#'   the data so that nested formulas are evaluated in the correct environment.
+#' @export
+#' @aliases uq uqs
+#' @examples
+#' f_interp(x ~ 1 + uq(1 + 2 + 3) + 10)
+#'
+#' # Use uqs() if you want to add multiple arguments to a function
+#' # It must evaluate to a list
+#' args <- list(1:10, na.rm = TRUE)
+#' f_interp(~ mean( uqs(args) ))
+#'
+#' # You can combine the two
+#' var <- quote(xyz)
+#' extra_args <- list(trim = 0.9)
+#' f_interp(~ mean( uq(var) , uqs(extra_args) ))
+#'
+#' foo <- function(n) {
+#'   ~ 1 + uq(n)
+#' }
+#' f <- foo(10)
+#' f
+#' f_interp(f)
+#' @useDynLib lazyeval interp_
+f_interp <- function(f, data = NULL) {
+  f_rhs(f) <- .Call(interp_, f_rhs(f), f_env(f), data)
+  f
+}
+
+#' @export
+#' @rdname f_interp
+uq <- function(x, data = NULL) {
+  if (is_formula(x)) {
+    if (is.null(data)) {
+      f_rhs(f_interp(x))
+    } else {
+      f_eval(x, data = data)
+    }
+  } else {
+    x
+  }
+}
+
+#' @export
+#' @rdname f_interp
+uqf <- function(x) {
+  if (!is_formula(x))
+    stop("`x` must be a formula", call. = FALSE)
+  x
+}
+
+#' @export
+#' @rdname f_interp
+uqs <- function(x) {
+  if (!is_vector(x)) {
+    stop("`x` must be a vector")
+  }
+
+  as.pairlist(x)
+}
diff --git a/R/formula.R b/R/formula.R
new file mode 100644
index 0000000..41445f2
--- /dev/null
+++ b/R/formula.R
@@ -0,0 +1,183 @@
+#' Create a formula object by "hand".
+#'
+#' @param lhs,rhs A call, name, or atomic vector.
+#' @param env An environment
+#' @return A formula object
+#' @export
+#' @examples
+#' f_new(quote(a))
+#' f_new(quote(a), quote(b))
+f_new <- function(rhs, lhs = NULL, env = parent.frame()) {
+  if (!is_lang(rhs)) {
+    stop("`rhs` must be a language object", call. = FALSE)
+  }
+  if (!is_lang(lhs) && !is.null(lhs)) {
+    stop("`lhs` must be a language object", call. = FALSE)
+  }
+  if (!is.environment(env)) {
+    stop("`env` must be an environment", call. = FALSE)
+  }
+
+  if (is.null(lhs)) {
+    f <- call_new("~", rhs)
+  } else {
+    f <- call_new("~", lhs, rhs)
+  }
+
+  structure(
+    f,
+    class = "formula",
+    .Environment = env
+  )
+}
+
+#' Is object a formula?
+#'
+#' @param x Object to test
+#' @export
+#' @examples
+#' is_formula(~ 10)
+#' is_formula(10)
+is_formula <- function(x) {
+  typeof(x) == "language" && inherits(x, "formula")
+}
+
+#' Get/set formula components.
+#'
+#' \code{f_rhs} extracts the righthand side, \code{f_lhs} extracts the
+#' lefthand side, and \code{f_env} extracts the environment. All functions
+#' throw an error if \code{f} is not a formula.
+#'
+#' @param f,x A formula
+#' @param value The value to replace with.
+#' @export
+#' @return \code{f_rhs} and \code{f_lhs} return language objects (i.e.
+#'   atomic vectors of length 1, a name, or a call). \code{f_env}
+#'   returns an environment.
+#' @examples
+#' f_rhs(~ 1 + 2 + 3)
+#' f_rhs(~ x)
+#' f_rhs(~ "A")
+#' f_rhs(1 ~ 2)
+#'
+#' f_lhs(~ y)
+#' f_lhs(x ~ y)
+#'
+#' f_env(~ x)
+#' @useDynLib lazyeval rhs
+f_rhs <- function(f) {
+  .Call(rhs, f)
+}
+
+#' @export
+#' @rdname f_rhs
+`f_rhs<-` <- function(x, value) {
+  stopifnot(is_formula(x))
+  f_new(value, f_lhs(x), f_env(x))
+}
+
+#' @export
+#' @rdname f_rhs
+#' @useDynLib lazyeval lhs
+f_lhs <- function(f) {
+  .Call(lhs, f)
+}
+
+#' @export
+#' @rdname f_rhs
+`f_lhs<-` <- function(x, value) {
+  stopifnot(is_formula(x))
+  f_new(f_rhs(x), value, f_env(x))
+}
+
+#' @export
+#' @rdname f_rhs
+#' @useDynLib lazyeval env
+f_env <- function(f) {
+  .Call(env, f)
+}
+
+#' @export
+#' @rdname f_rhs
+`f_env<-` <- function(x, value) {
+  stopifnot(is_formula(x))
+  f_new(f_rhs(x), f_lhs(x), value)
+}
+
+#' Turn RHS of formula into a string/label.
+#'
+#' Equivalent of \code{\link{expr_text}()} and \code{\link{expr_label}()} for
+#' formulas.
+#'
+#' @param x A formula.
+#' @inheritParams expr_text
+#' @export
+#' @examples
+#' f <- ~ a + b + bc
+#' f_text(f)
+#' f_label(f)
+#'
+#' # Names a quoted with ``
+#' f_label(~ x)
+#' # Strings are encoded
+#' f_label(~ "a\nb")
+#' # Long expressions are collapsed
+#' f_label(~ foo({
+#'   1 + 2
+#'   print(x)
+#' }))
+f_text <- function(x, width = 60L, nlines = Inf) {
+  expr_text_(f_rhs(x), width = width, nlines = nlines)
+}
+
+#' @export
+#' @rdname f_text
+f_label <- function(x) {
+  expr_label_(f_rhs(x))
+}
+
+#' Unwrap a formula
+#'
+#' This interpolates values in the formula that are defined in its environment,
+#' replacing the environment with its parent.
+#'
+#' @export
+#' @param f A formula to unwrap.
+#' @examples
+#' n <- 100
+#' f <- ~ x + n
+#' f_unwrap(f)
+f_unwrap <- function(f) {
+  stopifnot(is_formula(f))
+
+  e <- environment(f)
+  if (identical(e, emptyenv())) {
+    f
+  } else {
+    f_new(substitute_(f_rhs(f), e), f_lhs(f), parent.env(e))
+  }
+}
+
+#' Build a named list from the LHS of formulas
+#'
+#' \code{f_list} makes a new list; \code{as_f_list} takes an existing list.
+#' Both take the LHS of any two-sided formulas and evaluate it, replacing the
+#' current name with the result.
+#'
+#' @param ... Named arguments.
+#' @param x An existing list
+#' @return A named list.
+#' @export
+#' @useDynLib lazyeval lhs_name
+#' @examples
+#' f_list("y" ~ x)
+#' f_list(a = "y" ~ a, ~ b, c = ~c)
+f_list <- function(...) {
+  .Call(lhs_name, list(...))
+}
+
+#' @export
+#' @rdname f_list
+as_f_list <- function(x) {
+  .Call(lhs_name, x)
+}
diff --git a/R/function.R b/R/function.R
new file mode 100644
index 0000000..22905a0
--- /dev/null
+++ b/R/function.R
@@ -0,0 +1,35 @@
+#' Create a function by "hand"
+#'
+#' This constructs a new function given it's three components:
+#' list of arguments, body code and parent environment.
+#'
+#' @param args A named list of default arguments.  Note that if you want
+#'  arguments that don't have defaults, you'll need to use the special function
+#'  \code{\link{alist}}, e.g. \code{alist(a = , b = 1)}
+#' @param body A language object representing the code inside the function.
+#'   Usually this will be most easily generated with \code{\link{quote}}
+#' @param env The parent environment of the function, defaults to the calling
+#'  environment of \code{make_function}
+#' @export
+#' @examples
+#' f <- function(x) x + 3
+#' g <- function_new(alist(x = ), quote(x + 3))
+#'
+#' # The components of the functions are identical
+#' identical(formals(f), formals(g))
+#' identical(body(f), body(g))
+#' identical(environment(f), environment(g))
+#'
+#' # But the functions are not identical because f has src code reference
+#' identical(f, g)
+#'
+#' attr(f, "srcref") <- NULL
+#' # Now they are:
+#' stopifnot(identical(f, g))
+function_new <- function(args, body, env = parent.frame()) {
+  stopifnot(all(has_names(args)), is_lang(body), is.environment(env))
+
+  args <- as.pairlist(args)
+
+  eval(call("function", args, body), env)
+}
diff --git a/R/language.R b/R/language.R
new file mode 100644
index 0000000..ed51c3b
--- /dev/null
+++ b/R/language.R
@@ -0,0 +1,117 @@
+#' Is an object a language object?
+#'
+#' These helpers are consistent wrappers around their base R equivalents.
+#' A language object is either an atomic vector (typically a scalar), a
+#' name (aka a symbol), a call, or a pairlist (used for function arguments).
+#'
+#' @param x An object to test.
+#' @seealso \code{\link{as_name}()} and \code{\link{as_call}()} for coercion
+#'   functions.
+#' @export
+#' @examples
+#' q1 <- quote(1)
+#' is_lang(q1)
+#' is_atomic(q1)
+#'
+#' q2 <- quote(x)
+#' is_lang(q2)
+#' is_name(q2)
+#'
+#' q3 <- quote(x + 1)
+#' is_lang(q3)
+#' is_call(q3)
+is_lang <- function(x) {
+  is_call(x) || is_pairlist(x) || is_atomic(x) || is_name(x) || is.null(x)
+}
+
+#' @rdname is_lang
+#' @export
+is_name <- function(x) {
+  typeof(x) == "symbol"
+}
+
+#' @rdname is_lang
+#' @export
+is_call <- function(x) {
+  typeof(x) == "language"
+}
+
+#' @rdname is_lang
+#' @export
+is_pairlist <- function(x) {
+  typeof(x) == "pairlist"
+}
+
+#' @rdname is_lang
+#' @export
+is_atomic <- function(x) {
+  typeof(x) %in% c("logical", "integer", "double", "complex", "character", "raw")
+}
+
+
+#' Coerce an object to a name or call.
+#'
+#' These are a S3 generics with built-in methods for names, calls, formuals,
+#' and strings. The distinction between a name and a call is particularly
+#' important when coercing from a string. Coercing to a call will parse the
+#' string, coercing to a name will create a (potentially) non-syntactic name.
+#'
+#' @param x An object to coerce
+#' @export
+#' @examples
+#' as_name("x + y")
+#' as_call("x + y")
+#'
+#' as_call(~ f)
+#' as_name(~ f())
+as_name <- function(x) UseMethod("as_name")
+
+#' @export
+as_name.name <- function(x) x
+
+#' @export
+as_name.character <- function(x) {
+  if (length(x) > 1) {
+    stop("Can not coerce character vector of length > 1 to name", call. = FALSE)
+  }
+
+  as.name(x)
+}
+
+#' @export
+as_name.call <- function(x) x[[1]]
+
+#' @export
+as_name.formula <- function(x) {
+  as_name(f_rhs(x))
+}
+
+#' @export
+#' @rdname as_name
+as_call <- function(x) {
+  UseMethod("as_call")
+}
+
+#' @export
+as_call.name <- function(x) {
+  call_new(x)
+}
+
+#' @export
+as_call.call <- function(x) {
+  x
+}
+
+#' @export
+as_call.character <- function(x) {
+  if (length(x) > 1) {
+    stop("Can not coerce character vector of length > 1 to name", call. = FALSE)
+  }
+
+  parse(text = x)[[1]]
+}
+
+#' @export
+as_call.formula <- function(x) {
+  as_call(f_rhs(x))
+}
diff --git a/R/lazy-as.R b/R/lazy-as.R
new file mode 100644
index 0000000..5415aeb
--- /dev/null
+++ b/R/lazy-as.R
@@ -0,0 +1,109 @@
+#' Convert an object to a lazy expression or lazy dots.
+#'
+#' @param x An R object. Current methods for \code{as.lazy()} convert formulas,
+#'   character vectors, calls and names. Methods for \code{as.lazy_dots()}
+#'   convert lists and character vectors (by calling \code{\link{lapply}()}
+#'   with \code{as.lazy()}.)
+#' @param env Environment to use for objects that don't already have
+#'   associated environment.
+#' @export
+#' @examples
+#' as.lazy(~ x + 1)
+#' as.lazy(quote(x + 1), globalenv())
+#' as.lazy("x + 1", globalenv())
+#'
+#' as.lazy_dots(list(~x, y = ~z + 1))
+#' as.lazy_dots(c("a", "b", "c"), globalenv())
+#' as.lazy_dots(~x)
+#' as.lazy_dots(quote(x), globalenv())
+#' as.lazy_dots(quote(f()), globalenv())
+#' as.lazy_dots(lazy(x))
+as.lazy <- function(x, env = baseenv()) UseMethod("as.lazy")
+
+#' @export
+as.lazy.lazy <- function(x, env = baseenv()) x
+
+#' @export
+as.lazy.formula <- function(x, env = baseenv()) lazy_(x[[2]], environment(x))
+
+#' @export
+as.lazy.character <- function(x, env = baseenv()) lazy_(parse(text = x)[[1]], env)
+
+#' @export
+as.lazy.call <- function(x, env = baseenv()) lazy_(x, env)
+
+#' @export
+as.lazy.name <- function(x, env = baseenv()) lazy_(x, env)
+
+#' @export
+as.lazy.numeric <- function(x, env = baseenv()) {
+  if (length(x) > 1) {
+    warning("Truncating vector to length 1", call. = FALSE)
+    x <- x[1]
+  }
+  lazy_(x, env)
+}
+#' @export
+as.lazy.logical <- as.lazy.numeric
+
+#' @export
+#' @rdname as.lazy
+as.lazy_dots <- function(x, env) UseMethod("as.lazy_dots")
+
+#' @export
+as.lazy_dots.NULL <- function(x, env = baseenv()) {
+  structure(list(), class = "lazy_dots")
+}
+
+#' @export
+as.lazy_dots.list <- function(x, env = baseenv()) {
+  structure(lapply(x, as.lazy, env = env), class = "lazy_dots")
+}
+
+#' @export
+as.lazy_dots.name <- function(x, env = baseenv()) {
+  structure(list(as.lazy(x, env)), class = "lazy_dots")
+}
+#' @export
+as.lazy_dots.formula <- as.lazy_dots.name
+#' @export
+as.lazy_dots.call <- as.lazy_dots.name
+
+#' @export
+as.lazy_dots.lazy <- function(x, env = baseenv()) {
+  structure(list(x), class = "lazy_dots")
+}
+
+#' @export
+as.lazy_dots.character <- function(x, env = baseenv()) {
+  structure(lapply(x, as.lazy, env = env), class = "lazy_dots")
+}
+
+#' @export
+as.lazy_dots.lazy_dots <- function(x, env = baseenv()) {
+  x
+}
+
+#' Combine explicit and implicit dots.
+#'
+#' @param ... Individual lazy objects
+#' @param .dots A list of lazy objects
+#' @param all_named If \code{TRUE}, uses \code{\link{auto_name}} to ensure
+#'   every component has a name.
+#' @return A \code{\link{lazy_dots}}
+#' @keywords internal
+#' @export
+all_dots <- function(.dots, ..., all_named = FALSE) {
+  dots <- as.lazy_dots(list(...))
+  if (!missing(.dots)) {
+    dots2 <- as.lazy_dots(.dots)
+    dots <- c(dots, dots2)
+  }
+
+  if (all_named) {
+    dots <- auto_name(dots)
+  }
+
+  dots
+
+}
diff --git a/R/lazy-call.R b/R/lazy-call.R
new file mode 100644
index 0000000..c07a560
--- /dev/null
+++ b/R/lazy-call.R
@@ -0,0 +1,104 @@
+#' Make a call with \code{lazy_dots} as arguments.
+#'
+#' In order to exactly replay the original call, the environment must be the
+#' same for all of the dots. This function circumvents that a little,
+#' falling back to the \code{\link{baseenv}()} if all environments aren't
+#' the same.
+#'
+#' @param fun Function as symbol or quoted call.
+#' @param args Arguments to function; must be a \code{lazy_dots} object,
+#'   or something \code{\link{as.lazy_dots}()} can coerce..
+#' @return A list:
+#'   \item{env}{The common environment for all elements}
+#'   \item{expr}{The expression}
+#' @export
+#' @examples
+#' make_call(quote(f), lazy_dots(x = 1, 2))
+#' make_call(quote(f), list(x = 1, y = ~x))
+#' make_call(quote(f), ~x)
+#'
+#' # If no known or no common environment, fails back to baseenv()
+#' make_call(quote(f), quote(x))
+make_call <- function(fun, args) {
+  stopifnot(is.call(fun) || is.name(fun))
+  args <- as.lazy_dots(args)
+  expr <- lapply(args, `[[`, "expr")
+
+  lazy_(
+    as.call(c(fun, expr)),
+    common_env(args)
+  )
+}
+
+#' Find common environment in list of lazy objects.
+#'
+#' If no common environment is found, will return \code{baseenv()}.
+#'
+#' @param dots A list of lazy objects
+#' @keywords internal
+#' @export
+#' @examples
+#' common_env(lazy_dots(a, b, c))
+#'
+#' f <- function(x) ~x
+#' common_env(list(f(1)))
+#' common_env(list(f(1), f(2)))
+common_env <- function(dots) {
+  if (!is.list(dots)) stop("dots must be a list", call. = FALSE)
+  if (length(dots) == 0) return(baseenv())
+
+  dots <- as.lazy_dots(dots)
+  env <- dots[[1]]$env
+  if (length(dots) == 1) return(env)
+
+  for (i in 2:length(dots)) {
+    if (!identical(env, dots[[i]]$env)) {
+      return(baseenv())
+    }
+  }
+  env
+}
+
+# ------------------------------------------------------------------------------
+
+#' Evaluate a call with \code{lazy_dots} as argument.
+#'
+#' This simulates the original call as closely as possible by creating
+#' a temporary environment where each \code{lazy} object is bound to
+#' a promise by \code{\link{delayedAssign}}.
+#'
+#' @noRd
+#' @param env Environment in which to evaluate call. Defaults to
+#'   \code{\link{parent.frame}()}.
+#' @examples
+#' make_env <- function(...) list2env(list(...), parent = emptyenv())
+#'
+#' f1 <- as.lazy(quote(a()), make_env(a = function() {message("!"); 1}))
+#' f2 <- as.lazy(quote(a), make_env(a = 10))
+#' args <- as.lazy_dots(list(f1, f2))
+#'
+#' a <- 100
+#' eval_call(quote(`+`), args)
+eval_call <- function(fun, dots, env = parent.frame()) {
+
+  vars <- paste0("x", seq_along(dots))
+  names(vars) <- names(dots)
+
+  # Create environment containing promises
+  env <- new.env(parent = env)
+  for(i in seq_along(dots)) {
+    dot <- dots[[i]]
+
+    assign_call <- substitute(
+      delayedAssign(vars[i], expr, dot$env, assign.env = env),
+      list(expr = dot$expr)
+    )
+    eval(assign_call)
+  }
+
+  args <- lapply(vars, as.symbol)
+  call <- as.call(c(fun, args))
+
+  eval(call, env)
+}
+
diff --git a/R/lazy-dots.R b/R/lazy-dots.R
new file mode 100644
index 0000000..d18d1fd
--- /dev/null
+++ b/R/lazy-dots.R
@@ -0,0 +1,59 @@
+#' Capture ... (dots) for later lazy evaluation.
+#'
+#' @param ... Dots from another function
+#' @param .ignore_empty If \code{TRUE}, empty arguments will be ignored.
+#' @return A named list of \code{\link{lazy}} expressions.
+#' @inheritParams lazy
+#' @export
+#' @useDynLib lazyeval make_lazy_dots
+#' @examples
+#' lazy_dots(x = 1)
+#' lazy_dots(a, b, c * 4)
+#'
+#' f <- function(x = a + b, ...) {
+#'   lazy_dots(x = x, y = a + b, ...)
+#' }
+#' f(z = a + b)
+#' f(z = a + b, .follow_symbols = TRUE)
+#'
+#' # .follow_symbols is off by default because it causes problems
+#' # with lazy loaded objects
+#' lazy_dots(letters)
+#' lazy_dots(letters, .follow_symbols = TRUE)
+#'
+#' # You can also modify a dots like a list. Anything on the RHS will
+#' # be coerced to a lazy.
+#' l <- lazy_dots(x = 1)
+#' l$y <- quote(f)
+#' l[c("y", "x")]
+#' l["z"] <- list(~g)
+#'
+#' c(lazy_dots(x = 1), lazy_dots(f))
+lazy_dots <- function(..., .follow_symbols = FALSE, .ignore_empty = FALSE) {
+  .Call(make_lazy_dots, environment(), .follow_symbols, .ignore_empty)
+}
+
+is.lazy_dots <- function(x) inherits(x, "lazy_dots")
+
+#' @export
+`[.lazy_dots` <- function(x, i) {
+  structure(NextMethod(), class = "lazy_dots")
+}
+
+#' @export
+`$<-.lazy_dots` <- function(x, i, value) {
+  value <- as.lazy(value, parent.frame())
+  x[[i]] <- value
+  x
+}
+
+#' @export
+`[<-.lazy_dots` <- function(x, i, value) {
+  value <- lapply(value, as.lazy, env = parent.frame())
+  NextMethod()
+}
+
+#' @export
+c.lazy_dots <- function(..., recursive = FALSE) {
+  structure(NextMethod(), class = "lazy_dots")
+}
diff --git a/R/lazy-eval.R b/R/lazy-eval.R
new file mode 100644
index 0000000..ad0b142
--- /dev/null
+++ b/R/lazy-eval.R
@@ -0,0 +1,31 @@
+#' Evaluate a lazy expression.
+#'
+#' @param x A lazy object or a formula.
+#' @param data Option, a data frame or list in which to preferentially look
+#'   for variables before using the environment associated with the lazy
+#'   object.
+#' @export
+#' @examples
+#' f <- function(x) {
+#'   z <- 100
+#'   ~ x + z
+#' }
+#' z <- 10
+#' lazy_eval(f(10))
+#' lazy_eval(f(10), list(x = 100))
+#' lazy_eval(f(10), list(x = 1, z = 1))
+#'
+#' lazy_eval(lazy_dots(a = x, b = z), list(x = 10))
+lazy_eval <- function(x, data = NULL) {
+  if (is.lazy_dots(x)) {
+    return(lapply(x, lazy_eval, data = data))
+  }
+
+  x <- as.lazy(x)
+
+  if (!is.null(data)) {
+    eval(x$expr, data, x$env)
+  } else {
+    eval(x$expr, x$env, emptyenv())
+  }
+}
diff --git a/R/lazy-interp.R b/R/lazy-interp.R
new file mode 100644
index 0000000..bcc6768
--- /dev/null
+++ b/R/lazy-interp.R
@@ -0,0 +1,100 @@
+#' Interpolate values into an expression.
+#'
+#' This is useful if you want to build an expression up from a mixture of
+#' constants and variables.
+#'
+#' @param _obj An object to modify: can be a call, name, formula,
+#'   \code{\link{lazy}}, or a string.
+#' @param ...,.values Either individual name-value pairs, or a list
+#'   (or environment) of values.
+#' @export
+#' @examples
+#' # Interp works with formulas, lazy objects, quoted calls and strings
+#' interp(~ x + y, x = 10)
+#' interp(lazy(x + y), x = 10)
+#' interp(quote(x + y), x = 10)
+#' interp("x + y", x = 10)
+#'
+#' # Use as.name if you have a character string that gives a
+#' # variable name
+#' interp(~ mean(var), var = as.name("mpg"))
+#' # or supply the quoted name directly
+#' interp(~ mean(var), var = quote(mpg))
+#'
+#' # Or a function!
+#' interp(~ f(a, b), f = as.name("+"))
+#' # Remember every action in R is a function call:
+#' # http://adv-r.had.co.nz/Functions.html#all-calls
+#'
+#' # If you've built up a list of values through some other
+#' # mechanism, use .values
+#' interp(~ x + y, .values = list(x = 10))
+#'
+#' # You can also interpolate variables defined in the current
+#' # environment, but this is a little risky.
+#' y <- 10
+#' interp(~ x + y, .values = environment())
+interp <- function(`_obj`, ..., .values) {
+  UseMethod("interp")
+}
+
+#' @export
+interp.call <- function(`_obj`, ..., .values) {
+  values <- all_values(.values, ...)
+
+  substitute_(`_obj`, values)
+}
+
+#' @export
+interp.name <- function(`_obj`, ..., .values) {
+  values <- all_values(.values, ...)
+
+  substitute_(`_obj`, values)
+}
+
+#' @export
+interp.formula <- function(`_obj`, ..., .values) {
+  if (length(`_obj`) != 2)
+    stop("Must use one-sided formula.", call. = FALSE)
+
+  values <- all_values(.values, ...)
+
+  `_obj`[[2]] <- substitute_(`_obj`[[2]], values)
+  `_obj`
+}
+
+#' @export
+interp.lazy <- function(`_obj`, ..., .values) {
+  values <- all_values(.values, ...)
+
+  `_obj`$expr <-  substitute_(`_obj`$expr, values)
+  `_obj`
+}
+
+#' @export
+interp.character <- function(`_obj`, ..., .values) {
+  values <- all_values(.values, ...)
+
+  expr1 <- parse(text = `_obj`)[[1]]
+  expr2 <- substitute_(expr1, values)
+  paste(deparse(expr2), collapse = "\n")
+}
+
+all_values <- function(.values, ...) {
+  if (missing(.values)) {
+    values <- list(...)
+  } else if (identical(.values, globalenv())) {
+    # substitute doesn't want to replace in globalenv
+    values <- as.list(globalenv())
+  } else {
+    values <- .values
+  }
+
+  if (is.list(values)) {
+    # Replace lazy objects with their expressions
+    is_lazy <- vapply(values, is.lazy, logical(1))
+    values[is_lazy] <- lapply(values[is_lazy], `[[`, "expr")
+  }
+
+  values
+}
diff --git a/R/lazy-names.R b/R/lazy-names.R
new file mode 100644
index 0000000..3ba5ae5
--- /dev/null
+++ b/R/lazy-names.R
@@ -0,0 +1,43 @@
+#' Automatically name all components of a lazy dots.
+#'
+#' Any components missing a name will automatically get a name added by
+#' looking at the first \code{max_width} characters of the deparsed expression.
+#'
+#' @param x A \code{\link{lazy_dots}}
+#' @param max_width Maximum number of characters to use
+#' @keywords internal
+#' @export
+#' @examples
+#' x <- lazy_dots(1 + 2, mean(mpg))
+#' auto_name(x)
+#'
+#' auto_name(list(~f, quote(x)))
+auto_name <- function(x, max_width = 40) {
+  names(x) <- auto_names(x, max_width = max_width)
+  x
+}
+
+auto_names <- function(x, max_width = 40) {
+  x <- as.lazy_dots(x)
+
+  nms <- names(x) %||% rep("", length(x))
+
+  missing <- nms == ""
+  expr <- lapply(x[missing], `[[`, "expr")
+  nms[missing] <- vapply(expr, deparse_trunc, width = max_width,
+    FUN.VALUE = character(1), USE.NAMES = FALSE)
+
+  nms
+}
+
+deparse_trunc <- function(x, width = getOption("width")) {
+  if (is.symbol(x)) {
+    return(as.character(x))
+  }
+
+  text <- deparse(x, width.cutoff = width)
+  if (length(text) == 1 && nchar(text) < width) return(text)
+
+  paste0(substr(text[1], 1, width - 3), "...")
+}
+
diff --git a/R/lazy.R b/R/lazy.R
new file mode 100644
index 0000000..229ee53
--- /dev/null
+++ b/R/lazy.R
@@ -0,0 +1,68 @@
+#' Capture expression for later lazy evaluation.
+#'
+#' \code{lazy()} uses non-standard evaluation to turn promises into lazy
+#' objects; \code{lazy_()} does standard evaluation and is suitable for
+#' programming.
+#'
+#' Use \code{lazy()} like you'd use \code{\link{substitute}()}
+#' to capture an unevaluated promise. Compared to \code{substitute()} it
+#' also captures the environment associated with the promise, so that you
+#' can correctly replay it in the future.
+#'
+#' @param expr Expression to capture. For \code{lazy_} must be a name
+#'   or a call.
+#' @param env Environment in which to evaluate expr.
+#' @param .follow_symbols If \code{TRUE}, the default, follows promises across
+#'   function calls. See \code{vignette("chained-promises")} for details.
+#' @export
+#' @examples
+#' lazy_(quote(a + x), globalenv())
+#'
+#' # Lazy is designed to be used inside a function - you should
+#' # give it the name of a function argument (a promise)
+#' f <- function(x = b - a) {
+#'   lazy(x)
+#' }
+#' f()
+#' f(a + b / c)
+#'
+#' # Lazy also works when called from the global environment. This makes
+#' # easy to play with interactively.
+#' lazy(a + b / c)
+#'
+#' # By default, lazy will climb all the way back to the initial promise
+#' # This is handy if you have if you have nested functions:
+#' g <- function(y) f(y)
+#' h <- function(z) g(z)
+#' f(a + b)
+#' g(a + b)
+#' h(a + b)
+#'
+#' # To avoid this behavour, set .follow_symbols = FALSE
+#' # See vignette("chained-promises") for details
+lazy_ <- function(expr, env) {
+  stopifnot(is.call(expr) || is.name(expr) || is.atomic(expr))
+
+  structure(list(expr = expr, env = env), class = "lazy")
+}
+
+#' @rdname lazy_
+#' @export
+#' @useDynLib lazyeval make_lazy
+lazy <- function(expr, env = parent.frame(), .follow_symbols = TRUE) {
+  .Call(make_lazy, quote(expr), environment(), .follow_symbols)
+}
+
+is.lazy <- function(x) inherits(x, "lazy")
+
+#' @export
+print.lazy <- function(x, ...) {
+  code <- deparse(x$expr)
+  if (length(code) > 1) {
+    code <- paste(code[[1]], "...")
+  }
+
+  cat("<lazy>\n")
+  cat("  expr: ", code, "\n", sep = "")
+  cat("  env:  ", format(x$env), "\n", sep = "")
+}
diff --git a/R/utils.R b/R/utils.R
new file mode 100644
index 0000000..fe346ae
--- /dev/null
+++ b/R/utils.R
@@ -0,0 +1,37 @@
+"%||%" <- function(x, y) if(is.null(x)) y else x
+
+is_atomic <- function(x) {
+  typeof(x) %in% c("logical", "integer", "double", "complex", "character", "raw")
+}
+
+is_vector <- function(x) {
+  is_atomic(x) || is.list(x)
+}
+
+has_names <- function(x) {
+  nms <- names(x)
+  if (is.null(nms)) {
+    rep(FALSE, length(x))
+  } else {
+    !(is.na(nms) | nms == "")
+  }
+}
+
+substitute_ <- function(x, env) {
+  if (identical(env, globalenv())) {
+    env <- as.list(env)
+  }
+
+  call <- substitute(substitute(x, env), list(x = x))
+  eval(call)
+}
+
+#' Generate a missing argument.
+#'
+#' @export
+#' @examples
+#' f_interp(~f(x = uq(missing_arg())))
+#' f_interp(~f(x = uq(NULL)))
+missing_arg <- function() {
+  quote(expr = )
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f3c7237
--- /dev/null
+++ b/README.md
@@ -0,0 +1,21 @@
+# lazyeval
+
+[![Build Status](https://travis-ci.org/hadley/lazyeval.png?branch=master)](https://travis-ci.org/hadley/lazyeval)
+[![Coverage Status](https://img.shields.io/codecov/c/github/hadley/lazyeval/master.svg)](https://codecov.io/github/hadley/lazyeval?branch=master)
+
+The lazyeval package provides tools that make it easier to correctly implement non-standard evaluation (NSE) in R. You use lazy evaluation by requiring the user to "quote" specially evaluated arguments with `~`, and then using the lazyeval package to compute with those formulas. It is also possible to eliminate the use of the `~` by converting promises to formulas. This does make programming with such functions a little harder, but it can be worth it in certain situations. See `vignette( [...]
+
+## Installation
+
+Install the released version from CRAN with:
+
+```R
+install.packages("lazyeval")
+```
+
+Install the development version from github with:
+
+```R
+# install.packages("devtools")
+devtools::install_github("hadley/lazyeval", build_vignettes = TRUE)
+```
diff --git a/build/vignette.rds b/build/vignette.rds
new file mode 100644
index 0000000..fe95161
Binary files /dev/null and b/build/vignette.rds differ
diff --git a/debian/README.test b/debian/README.test
deleted file mode 100644
index 8d70ca3..0000000
--- a/debian/README.test
+++ /dev/null
@@ -1,9 +0,0 @@
-Notes on how this package can be tested.
-────────────────────────────────────────
-
-This package can be tested by running the provided test:
-
-cd tests
-LC_ALL=C R --no-save < testthat.R
-
-in order to confirm its integrity.
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index fad9cf6..0000000
--- a/debian/changelog
+++ /dev/null
@@ -1,13 +0,0 @@
-r-cran-lazyeval (0.2.0-1) unstable; urgency=medium
-
-  * New upstream version
-  * Convert to dh-r
-  * Canonical homepage for CRAN
-
- -- Andreas Tille <tille at debian.org>  Tue, 08 Nov 2016 12:29:15 +0100
-
-r-cran-lazyeval (0.1.10-1) unstable; urgency=low
-
-  * Initial release (Closes: #818980)
-
- -- Andreas Tille <tille at debian.org>  Tue, 22 Mar 2016 16:00:36 +0100
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index ec63514..0000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-9
diff --git a/debian/control b/debian/control
deleted file mode 100644
index f5b724b..0000000
--- a/debian/control
+++ /dev/null
@@ -1,23 +0,0 @@
-Source: r-cran-lazyeval
-Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.org>
-Uploaders: Andreas Tille <tille at debian.org>
-Section: gnu-r
-Priority: optional
-Build-Depends: debhelper (>= 9),
-               dh-r,
-               r-base-dev
-Standards-Version: 3.9.8
-Vcs-Browser: https://anonscm.debian.org/viewvc/debian-med/trunk/packages/R/r-cran-lazyeval/trunk/
-Vcs-Svn: svn://anonscm.debian.org/debian-med/trunk/packages/R/r-cran-lazyeval/trunk/
-Homepage: https://cran.r-project.org/package=lazyeval
-
-Package: r-cran-lazyeval
-Architecture: any
-Depends: ${misc:Depends},
-         ${shlibs:Depends},
-         ${R:Depends}
-Recommends: ${R:Recommends}
-Suggests: ${R:Suggests}
-Description: GNU R lazy (non-standard) evaluation
- This GNU R package provides A disciplined approach to non-standard
- evaluation.
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index 8eb9df3..0000000
--- a/debian/copyright
+++ /dev/null
@@ -1,28 +0,0 @@
-Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
-Upstream-Contact: Hadley Wickham <hadley at rstudio.com>
-Upstream-Name: lazyeval
-Source: https://cran.r-project.org/package=lazyeval
-
-Files: *
-Copyright: 2013-2016 Hadley Wickham <hadley at rstudio.com>
-License: GPL-3
-
-Files: debian/*
-Copyright: 2016 Andreas Tille <tille at debian.org>
-License: GPL-3
-
-License: GPL-3
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, version 3 of the License.
- .
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
- .
- You should have received a copy of the GNU General Public License
- along with this program.  If not, see <http://www.gnu.org/licenses/>.
- .
-  On a Debian system, the GNU GPL license is distributed in the file
- ‘/usr/share/common-licenses/GPL-3'.
diff --git a/debian/docs b/debian/docs
deleted file mode 100644
index 960011c..0000000
--- a/debian/docs
+++ /dev/null
@@ -1,3 +0,0 @@
-tests
-debian/README.test
-debian/tests/run-unit-test
diff --git a/debian/rules b/debian/rules
deleted file mode 100755
index 68d9a36..0000000
--- a/debian/rules
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/make -f
-
-%:
-	dh $@ --buildsystem R
diff --git a/debian/source/format b/debian/source/format
deleted file mode 100644
index 163aaf8..0000000
--- a/debian/source/format
+++ /dev/null
@@ -1 +0,0 @@
-3.0 (quilt)
diff --git a/debian/tests/control b/debian/tests/control
deleted file mode 100644
index b044b0c..0000000
--- a/debian/tests/control
+++ /dev/null
@@ -1,3 +0,0 @@
-Tests: run-unit-test
-Depends: @, r-cran-testthat
-Restrictions: allow-stderr
diff --git a/debian/tests/run-unit-test b/debian/tests/run-unit-test
deleted file mode 100644
index 29fdae7..0000000
--- a/debian/tests/run-unit-test
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/sh -e
-
-oname=lazyeval
-pkg=r-cran-`echo $oname | tr [A-Z] [a-z]`
-
-if [ "$ADTTMP" = "" ] ; then
-  ADTTMP=`mktemp -d /tmp/${pkg}-test.XXXXXX`
-fi
-cd $ADTTMP
-cp -a /usr/share/doc/${pkg}/tests/* $ADTTMP
-LC_ALL=C R --no-save < testthat.R
-rm -fr $ADTTMP/*
diff --git a/debian/watch b/debian/watch
deleted file mode 100644
index cf4ca5a..0000000
--- a/debian/watch
+++ /dev/null
@@ -1,3 +0,0 @@
-version=3
-http://cran.r-project.org/src/contrib/lazyeval_([-0-9\.]*).tar.gz
-
diff --git a/inst/doc/lazyeval-old.R b/inst/doc/lazyeval-old.R
new file mode 100644
index 0000000..1278589
--- /dev/null
+++ b/inst/doc/lazyeval-old.R
@@ -0,0 +1,107 @@
+## ---- echo = FALSE-------------------------------------------------------
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+rownames(mtcars) <- NULL
+
+## ------------------------------------------------------------------------
+library(lazyeval)
+f <- function(x = a - b) {
+  lazy(x)
+}
+f()
+f(a + b)
+
+## ------------------------------------------------------------------------
+a <- 10
+b <- 1
+lazy_eval(f())
+lazy_eval(f(a + b))
+
+## ------------------------------------------------------------------------
+lazy_eval(f(), list(a = 1))
+
+## ------------------------------------------------------------------------
+lazy_eval(~ a + b)
+h <- function(i) {
+  ~ 10 + i
+}
+lazy_eval(h(1))
+
+## ------------------------------------------------------------------------
+subset2_ <- function(df, condition) {
+  r <- lazy_eval(condition, df)
+  r <- r & !is.na(r)
+  df[r, , drop = FALSE]
+} 
+
+subset2_(mtcars, lazy(mpg > 31))
+
+## ------------------------------------------------------------------------
+subset2_(mtcars, ~mpg > 31)
+subset2_(mtcars, quote(mpg > 31))
+subset2_(mtcars, "mpg > 31")
+
+## ------------------------------------------------------------------------
+subset2 <- function(df, condition) {
+  subset2_(df, lazy(condition))
+}
+subset2(mtcars, mpg > 31)
+
+## ------------------------------------------------------------------------
+above_threshold <- function(df, var, threshold) {
+  cond <- interp(~ var > x, var = lazy(var), x = threshold)
+  subset2_(df, cond)
+}
+above_threshold(mtcars, mpg, 31)
+
+## ------------------------------------------------------------------------
+x <- 31
+f1 <- function(...) {
+  x <- 30
+  subset(mtcars, ...)
+}
+# Uses 30 instead of 31
+f1(mpg > x)
+
+f2 <- function(...) {
+  x <- 30
+  subset2(mtcars, ...)
+}
+# Correctly uses 31
+f2(mpg > x)
+
+## ---- eval = FALSE-------------------------------------------------------
+#  x <- 31
+#  g1 <- function(comp) {
+#    x <- 30
+#    subset(mtcars, comp)
+#  }
+#  g1(mpg > x)
+#  #> Error: object 'mpg' not found
+
+## ------------------------------------------------------------------------
+g2 <- function(comp) {
+  x <- 30
+  subset2(mtcars, comp)
+}
+g2(mpg > x)
+
+## ------------------------------------------------------------------------
+library(lazyeval)
+f1 <- function(x) lazy(x)
+g1 <- function(y) f1(y)
+
+g1(a + b)
+
+## ------------------------------------------------------------------------
+f2 <- function(x) lazy(x, .follow_symbols = FALSE)
+g2 <- function(y) f2(y)
+
+g2(a + b)
+
+## ------------------------------------------------------------------------
+a <- 10
+b <- 1
+
+lazy_eval(g1(a + b))
+lazy_eval(g2(a + b))
+
diff --git a/inst/doc/lazyeval-old.Rmd b/inst/doc/lazyeval-old.Rmd
new file mode 100644
index 0000000..35b0108
--- /dev/null
+++ b/inst/doc/lazyeval-old.Rmd
@@ -0,0 +1,212 @@
+---
+title: "Lazyeval: a new approach to NSE"
+date: "`r Sys.Date()`"
+output: rmarkdown::html_vignette
+vignette: >
+  %\VignetteIndexEntry{Lazyeval: a new approach to NSE}
+  %\VignetteEngine{knitr::rmarkdown}
+  %\usepackage[utf8]{inputenc}
+---
+
+```{r, echo = FALSE}
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+rownames(mtcars) <- NULL
+```
+
+This document outlines my previous approach to non-standard evaluation (NSE). You should avoid it unless you are working with an older version of dplyr or tidyr.
+
+There are three key ideas:
+
+* Instead of using `substitute()`, use `lazyeval::lazy()` to capture both expression
+  and environment. (Or use `lazyeval::lazy_dots(...)` to capture promises in `...`)
+  
+* Every function that uses NSE should have a standard evaluation (SE) escape 
+  hatch that does the actual computation. The SE-function name should end with 
+  `_`.
+  
+* The SE-function has a flexible input specification to make it easy for people
+  to program with.
+
+## `lazy()`
+
+The key tool that makes this approach possible is `lazy()`, an equivalent to `substitute()` that captures both expression and environment associated with a function argument:
+
+```{r}
+library(lazyeval)
+f <- function(x = a - b) {
+  lazy(x)
+}
+f()
+f(a + b)
+```
+
+As a complement to `eval()`, the lazy package provides `lazy_eval()` that uses the environment associated with the lazy object:
+
+```{r}
+a <- 10
+b <- 1
+lazy_eval(f())
+lazy_eval(f(a + b))
+```
+
+The second argument to lazy eval is a list or data frame where names should be looked up first:
+
+```{r}
+lazy_eval(f(), list(a = 1))
+```
+
+`lazy_eval()` also works with formulas, since they contain the same information as a lazy object: an expression (only the RHS is used by convention) and an environment:
+
+```{r}
+lazy_eval(~ a + b)
+h <- function(i) {
+  ~ 10 + i
+}
+lazy_eval(h(1))
+```
+
+## Standard evaluation
+
+Whenever we need a function that does non-standard evaluation, always write the standard evaluation version first. For example, let's implement our own version of `subset()`:
+
+```{r}
+subset2_ <- function(df, condition) {
+  r <- lazy_eval(condition, df)
+  r <- r & !is.na(r)
+  df[r, , drop = FALSE]
+} 
+
+subset2_(mtcars, lazy(mpg > 31))
+```
+
+`lazy_eval()` will always coerce it's first argument into a lazy object, so a variety of specifications will work:
+
+```{r}
+subset2_(mtcars, ~mpg > 31)
+subset2_(mtcars, quote(mpg > 31))
+subset2_(mtcars, "mpg > 31")
+```
+
+Note that quoted called and strings don't have environments associated with them, so `as.lazy()` defaults to using `baseenv()`. This will work if the expression is self-contained (i.e. doesn't contain any references to variables in the local environment), and will otherwise fail quickly and robustly.
+
+## Non-standard evaluation
+
+With the SE version in hand, writing the NSE version is easy. We just use `lazy()` to capture the unevaluated expression and corresponding environment:
+
+```{r}
+subset2 <- function(df, condition) {
+  subset2_(df, lazy(condition))
+}
+subset2(mtcars, mpg > 31)
+```
+
+This standard evaluation escape hatch is very important because it allows us to implement different NSE approaches. For example, we could create a subsetting function that finds all rows where a variable is above a threshold:
+
+```{r}
+above_threshold <- function(df, var, threshold) {
+  cond <- interp(~ var > x, var = lazy(var), x = threshold)
+  subset2_(df, cond)
+}
+above_threshold(mtcars, mpg, 31)
+```
+
+Here we're using `interp()` to modify a formula. We use the value of `threshold` and the expression in  by `var`.
+
+## Scoping
+
+Because `lazy()` captures the environment associated with the function argument, we automatically avoid a subtle scoping bug present in `subset()`:
+  
+```{r}
+x <- 31
+f1 <- function(...) {
+  x <- 30
+  subset(mtcars, ...)
+}
+# Uses 30 instead of 31
+f1(mpg > x)
+
+f2 <- function(...) {
+  x <- 30
+  subset2(mtcars, ...)
+}
+# Correctly uses 31
+f2(mpg > x)
+```
+
+`lazy()` has another advantage over `substitute()` - by default, it follows promises across function invocations. This simplifies the casual use of NSE.
+
+```{r, eval = FALSE}
+x <- 31
+g1 <- function(comp) {
+  x <- 30
+  subset(mtcars, comp)
+}
+g1(mpg > x)
+#> Error: object 'mpg' not found
+```
+
+```{r}
+g2 <- function(comp) {
+  x <- 30
+  subset2(mtcars, comp)
+}
+g2(mpg > x)
+```
+
+Note that `g2()` doesn't have a standard-evaluation escape hatch, so it's not suitable for programming with in the same way that `subset2_()` is. 
+
+## Chained promises
+
+Take the following example:
+
+```{r}
+library(lazyeval)
+f1 <- function(x) lazy(x)
+g1 <- function(y) f1(y)
+
+g1(a + b)
+```
+
+`lazy()` returns `a + b` because it always tries to find the top-level promise.
+
+In this case the process looks like this:
+
+1. Find the object that `x` is bound to.
+2. It's a promise, so find the expr it's bound to (`y`, a symbol) and the
+   environment in which it should be evaluated (the environment of `g()`).
+3. Since `x` is bound to a symbol, look up its value: it's bound to a promise.
+4. That promise has expression `a + b` and should be evaluated in the global
+   environment.
+5. The expression is not a symbol, so stop.
+
+Occasionally, you want to avoid this recursive behaviour, so you can use `follow_symbol = FALSE`:
+
+```{r}
+f2 <- function(x) lazy(x, .follow_symbols = FALSE)
+g2 <- function(y) f2(y)
+
+g2(a + b)
+```
+
+Either way, if you evaluate the lazy expression you'll get the same result:
+
+```{r}
+a <- 10
+b <- 1
+
+lazy_eval(g1(a + b))
+lazy_eval(g2(a + b))
+```
+
+Note that the resolution of chained promises only works with unevaluated objects. This is because R deletes the information about the environment associated with a promise when it has been forced, so that the garbage collector is allowed to remove the environment from memory in case it is no longer used. `lazy()` will fail with an error in such situations.
+
+```{r, error = TRUE, purl = FALSE}
+var <- 0
+
+f3 <- function(x) {
+  force(x)
+  lazy(x)
+}
+
+f3(var)
+```
diff --git a/inst/doc/lazyeval-old.html b/inst/doc/lazyeval-old.html
new file mode 100644
index 0000000..6a6b2fb
--- /dev/null
+++ b/inst/doc/lazyeval-old.html
@@ -0,0 +1,269 @@
+<!DOCTYPE html>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+
+<meta charset="utf-8">
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="pandoc" />
+
+<meta name="viewport" content="width=device-width, initial-scale=1">
+
+
+<meta name="date" content="2016-06-10" />
+
+<title>Lazyeval: a new approach to NSE</title>
+
+
+
+<style type="text/css">code{white-space: pre;}</style>
+<style type="text/css">
+div.sourceCode { overflow-x: auto; }
+table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
+  margin: 0; padding: 0; vertical-align: baseline; border: none; }
+table.sourceCode { width: 100%; line-height: 100%; }
+td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
+td.sourceCode { padding-left: 5px; }
+code > span.kw { color: #007020; font-weight: bold; } /* Keyword */
+code > span.dt { color: #902000; } /* DataType */
+code > span.dv { color: #40a070; } /* DecVal */
+code > span.bn { color: #40a070; } /* BaseN */
+code > span.fl { color: #40a070; } /* Float */
+code > span.ch { color: #4070a0; } /* Char */
+code > span.st { color: #4070a0; } /* String */
+code > span.co { color: #60a0b0; font-style: italic; } /* Comment */
+code > span.ot { color: #007020; } /* Other */
+code > span.al { color: #ff0000; font-weight: bold; } /* Alert */
+code > span.fu { color: #06287e; } /* Function */
+code > span.er { color: #ff0000; font-weight: bold; } /* Error */
+code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
+code > span.cn { color: #880000; } /* Constant */
+code > span.sc { color: #4070a0; } /* SpecialChar */
+code > span.vs { color: #4070a0; } /* VerbatimString */
+code > span.ss { color: #bb6688; } /* SpecialString */
+code > span.im { } /* Import */
+code > span.va { color: #19177c; } /* Variable */
+code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
+code > span.op { color: #666666; } /* Operator */
+code > span.bu { } /* BuiltIn */
+code > span.ex { } /* Extension */
+code > span.pp { color: #bc7a00; } /* Preprocessor */
+code > span.at { color: #7d9029; } /* Attribute */
+code > span.do { color: #ba2121; font-style: italic; } /* Documentation */
+code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
+code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
+code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
+</style>
+
+
+
+<link href="data:text/css;charset=utf-8,body%20%7B%0Abackground%2Dcolor%3A%20%23fff%3B%0Amargin%3A%201em%20auto%3B%0Amax%2Dwidth%3A%20700px%3B%0Aoverflow%3A%20visible%3B%0Apadding%2Dleft%3A%202em%3B%0Apadding%2Dright%3A%202em%3B%0Afont%2Dfamily%3A%20%22Open%20Sans%22%2C%20%22Helvetica%20Neue%22%2C%20Helvetica%2C%20Arial%2C%20sans%2Dserif%3B%0Afont%2Dsize%3A%2014px%3B%0Aline%2Dheight%3A%201%2E35%3B%0A%7D%0A%23header%20%7B%0Atext%2Dalign%3A%20center%3B%0A%7D%0A%23TOC%20%7B%0Aclear%3A%20bot [...]
+
+</head>
+
+<body>
+
+
+
+
+<h1 class="title toc-ignore">Lazyeval: a new approach to NSE</h1>
+<h4 class="date"><em>2016-06-10</em></h4>
+
+
+
+<p>This document outlines my previous approach to non-standard evaluation (NSE). You should avoid it unless you are working with an older version of dplyr or tidyr.</p>
+<p>There are three key ideas:</p>
+<ul>
+<li><p>Instead of using <code>substitute()</code>, use <code>lazyeval::lazy()</code> to capture both expression and environment. (Or use <code>lazyeval::lazy_dots(...)</code> to capture promises in <code>...</code>)</p></li>
+<li><p>Every function that uses NSE should have a standard evaluation (SE) escape hatch that does the actual computation. The SE-function name should end with <code>_</code>.</p></li>
+<li><p>The SE-function has a flexible input specification to make it easy for people to program with.</p></li>
+</ul>
+<div id="lazy" class="section level2">
+<h2><code>lazy()</code></h2>
+<p>The key tool that makes this approach possible is <code>lazy()</code>, an equivalent to <code>substitute()</code> that captures both expression and environment associated with a function argument:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">library</span>(lazyeval)
+f <-<span class="st"> </span>function(<span class="dt">x =</span> a -<span class="st"> </span>b) {
+  <span class="kw">lazy</span>(x)
+}
+<span class="kw">f</span>()
+<span class="co">#> <lazy></span>
+<span class="co">#>   expr: a - b</span>
+<span class="co">#>   env:  <environment: 0x7fd4dbb11ff0></span>
+<span class="kw">f</span>(a +<span class="st"> </span>b)
+<span class="co">#> <lazy></span>
+<span class="co">#>   expr: a + b</span>
+<span class="co">#>   env:  <environment: R_GlobalEnv></span></code></pre></div>
+<p>As a complement to <code>eval()</code>, the lazy package provides <code>lazy_eval()</code> that uses the environment associated with the lazy object:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">a <-<span class="st"> </span><span class="dv">10</span>
+b <-<span class="st"> </span><span class="dv">1</span>
+<span class="kw">lazy_eval</span>(<span class="kw">f</span>())
+<span class="co">#> [1] 9</span>
+<span class="kw">lazy_eval</span>(<span class="kw">f</span>(a +<span class="st"> </span>b))
+<span class="co">#> [1] 11</span></code></pre></div>
+<p>The second argument to lazy eval is a list or data frame where names should be looked up first:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">lazy_eval</span>(<span class="kw">f</span>(), <span class="kw">list</span>(<span class="dt">a =</span> <span class="dv">1</span>))
+<span class="co">#> [1] 0</span></code></pre></div>
+<p><code>lazy_eval()</code> also works with formulas, since they contain the same information as a lazy object: an expression (only the RHS is used by convention) and an environment:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">lazy_eval</span>(~<span class="st"> </span>a +<span class="st"> </span>b)
+<span class="co">#> [1] 11</span>
+h <-<span class="st"> </span>function(i) {
+  ~<span class="st"> </span><span class="dv">10</span> +<span class="st"> </span>i
+}
+<span class="kw">lazy_eval</span>(<span class="kw">h</span>(<span class="dv">1</span>))
+<span class="co">#> [1] 11</span></code></pre></div>
+</div>
+<div id="standard-evaluation" class="section level2">
+<h2>Standard evaluation</h2>
+<p>Whenever we need a function that does non-standard evaluation, always write the standard evaluation version first. For example, let’s implement our own version of <code>subset()</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">subset2_ <-<span class="st"> </span>function(df, condition) {
+  r <-<span class="st"> </span><span class="kw">lazy_eval</span>(condition, df)
+  r <-<span class="st"> </span>r &<span class="st"> </span>!<span class="kw">is.na</span>(r)
+  df[r, , drop =<span class="st"> </span><span class="ot">FALSE</span>]
+} 
+
+<span class="kw">subset2_</span>(mtcars, <span class="kw">lazy</span>(mpg ><span class="st"> </span><span class="dv">31</span>))
+<span class="co">#>     mpg cyl disp hp drat    wt  qsec vs am gear carb</span>
+<span class="co">#> 18 32.4   4 78.7 66 4.08 2.200 19.47  1  1    4    1</span>
+<span class="co">#> 20 33.9   4 71.1 65 4.22 1.835 19.90  1  1    4    1</span></code></pre></div>
+<p><code>lazy_eval()</code> will always coerce it’s first argument into a lazy object, so a variety of specifications will work:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">subset2_</span>(mtcars, ~mpg ><span class="st"> </span><span class="dv">31</span>)
+<span class="co">#>     mpg cyl disp hp drat    wt  qsec vs am gear carb</span>
+<span class="co">#> 18 32.4   4 78.7 66 4.08 2.200 19.47  1  1    4    1</span>
+<span class="co">#> 20 33.9   4 71.1 65 4.22 1.835 19.90  1  1    4    1</span>
+<span class="kw">subset2_</span>(mtcars, <span class="kw">quote</span>(mpg ><span class="st"> </span><span class="dv">31</span>))
+<span class="co">#>     mpg cyl disp hp drat    wt  qsec vs am gear carb</span>
+<span class="co">#> 18 32.4   4 78.7 66 4.08 2.200 19.47  1  1    4    1</span>
+<span class="co">#> 20 33.9   4 71.1 65 4.22 1.835 19.90  1  1    4    1</span>
+<span class="kw">subset2_</span>(mtcars, <span class="st">"mpg > 31"</span>)
+<span class="co">#>     mpg cyl disp hp drat    wt  qsec vs am gear carb</span>
+<span class="co">#> 18 32.4   4 78.7 66 4.08 2.200 19.47  1  1    4    1</span>
+<span class="co">#> 20 33.9   4 71.1 65 4.22 1.835 19.90  1  1    4    1</span></code></pre></div>
+<p>Note that quoted called and strings don’t have environments associated with them, so <code>as.lazy()</code> defaults to using <code>baseenv()</code>. This will work if the expression is self-contained (i.e. doesn’t contain any references to variables in the local environment), and will otherwise fail quickly and robustly.</p>
+</div>
+<div id="non-standard-evaluation" class="section level2">
+<h2>Non-standard evaluation</h2>
+<p>With the SE version in hand, writing the NSE version is easy. We just use <code>lazy()</code> to capture the unevaluated expression and corresponding environment:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">subset2 <-<span class="st"> </span>function(df, condition) {
+  <span class="kw">subset2_</span>(df, <span class="kw">lazy</span>(condition))
+}
+<span class="kw">subset2</span>(mtcars, mpg ><span class="st"> </span><span class="dv">31</span>)
+<span class="co">#>     mpg cyl disp hp drat    wt  qsec vs am gear carb</span>
+<span class="co">#> 18 32.4   4 78.7 66 4.08 2.200 19.47  1  1    4    1</span>
+<span class="co">#> 20 33.9   4 71.1 65 4.22 1.835 19.90  1  1    4    1</span></code></pre></div>
+<p>This standard evaluation escape hatch is very important because it allows us to implement different NSE approaches. For example, we could create a subsetting function that finds all rows where a variable is above a threshold:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">above_threshold <-<span class="st"> </span>function(df, var, threshold) {
+  cond <-<span class="st"> </span><span class="kw">interp</span>(~<span class="st"> </span>var ><span class="st"> </span>x, <span class="dt">var =</span> <span class="kw">lazy</span>(var), <span class="dt">x =</span> threshold)
+  <span class="kw">subset2_</span>(df, cond)
+}
+<span class="kw">above_threshold</span>(mtcars, mpg, <span class="dv">31</span>)
+<span class="co">#>     mpg cyl disp hp drat    wt  qsec vs am gear carb</span>
+<span class="co">#> 18 32.4   4 78.7 66 4.08 2.200 19.47  1  1    4    1</span>
+<span class="co">#> 20 33.9   4 71.1 65 4.22 1.835 19.90  1  1    4    1</span></code></pre></div>
+<p>Here we’re using <code>interp()</code> to modify a formula. We use the value of <code>threshold</code> and the expression in by <code>var</code>.</p>
+</div>
+<div id="scoping" class="section level2">
+<h2>Scoping</h2>
+<p>Because <code>lazy()</code> captures the environment associated with the function argument, we automatically avoid a subtle scoping bug present in <code>subset()</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">x <-<span class="st"> </span><span class="dv">31</span>
+f1 <-<span class="st"> </span>function(...) {
+  x <-<span class="st"> </span><span class="dv">30</span>
+  <span class="kw">subset</span>(mtcars, ...)
+}
+<span class="co"># Uses 30 instead of 31</span>
+<span class="kw">f1</span>(mpg ><span class="st"> </span>x)
+<span class="co">#>     mpg cyl disp  hp drat    wt  qsec vs am gear carb</span>
+<span class="co">#> 18 32.4   4 78.7  66 4.08 2.200 19.47  1  1    4    1</span>
+<span class="co">#> 19 30.4   4 75.7  52 4.93 1.615 18.52  1  1    4    2</span>
+<span class="co">#> 20 33.9   4 71.1  65 4.22 1.835 19.90  1  1    4    1</span>
+<span class="co">#> 28 30.4   4 95.1 113 3.77 1.513 16.90  1  1    5    2</span>
+
+f2 <-<span class="st"> </span>function(...) {
+  x <-<span class="st"> </span><span class="dv">30</span>
+  <span class="kw">subset2</span>(mtcars, ...)
+}
+<span class="co"># Correctly uses 31</span>
+<span class="kw">f2</span>(mpg ><span class="st"> </span>x)
+<span class="co">#>     mpg cyl disp hp drat    wt  qsec vs am gear carb</span>
+<span class="co">#> 18 32.4   4 78.7 66 4.08 2.200 19.47  1  1    4    1</span>
+<span class="co">#> 20 33.9   4 71.1 65 4.22 1.835 19.90  1  1    4    1</span></code></pre></div>
+<p><code>lazy()</code> has another advantage over <code>substitute()</code> - by default, it follows promises across function invocations. This simplifies the casual use of NSE.</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">x <-<span class="st"> </span><span class="dv">31</span>
+g1 <-<span class="st"> </span>function(comp) {
+  x <-<span class="st"> </span><span class="dv">30</span>
+  <span class="kw">subset</span>(mtcars, comp)
+}
+<span class="kw">g1</span>(mpg ><span class="st"> </span>x)
+<span class="co">#> Error: object 'mpg' not found</span></code></pre></div>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">g2 <-<span class="st"> </span>function(comp) {
+  x <-<span class="st"> </span><span class="dv">30</span>
+  <span class="kw">subset2</span>(mtcars, comp)
+}
+<span class="kw">g2</span>(mpg ><span class="st"> </span>x)
+<span class="co">#>     mpg cyl disp hp drat    wt  qsec vs am gear carb</span>
+<span class="co">#> 18 32.4   4 78.7 66 4.08 2.200 19.47  1  1    4    1</span>
+<span class="co">#> 20 33.9   4 71.1 65 4.22 1.835 19.90  1  1    4    1</span></code></pre></div>
+<p>Note that <code>g2()</code> doesn’t have a standard-evaluation escape hatch, so it’s not suitable for programming with in the same way that <code>subset2_()</code> is.</p>
+</div>
+<div id="chained-promises" class="section level2">
+<h2>Chained promises</h2>
+<p>Take the following example:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">library</span>(lazyeval)
+f1 <-<span class="st"> </span>function(x) <span class="kw">lazy</span>(x)
+g1 <-<span class="st"> </span>function(y) <span class="kw">f1</span>(y)
+
+<span class="kw">g1</span>(a +<span class="st"> </span>b)
+<span class="co">#> <lazy></span>
+<span class="co">#>   expr: a + b</span>
+<span class="co">#>   env:  <environment: R_GlobalEnv></span></code></pre></div>
+<p><code>lazy()</code> returns <code>a + b</code> because it always tries to find the top-level promise.</p>
+<p>In this case the process looks like this:</p>
+<ol style="list-style-type: decimal">
+<li>Find the object that <code>x</code> is bound to.</li>
+<li>It’s a promise, so find the expr it’s bound to (<code>y</code>, a symbol) and the environment in which it should be evaluated (the environment of <code>g()</code>).</li>
+<li>Since <code>x</code> is bound to a symbol, look up its value: it’s bound to a promise.</li>
+<li>That promise has expression <code>a + b</code> and should be evaluated in the global environment.</li>
+<li>The expression is not a symbol, so stop.</li>
+</ol>
+<p>Occasionally, you want to avoid this recursive behaviour, so you can use <code>follow_symbol = FALSE</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">f2 <-<span class="st"> </span>function(x) <span class="kw">lazy</span>(x, <span class="dt">.follow_symbols =</span> <span class="ot">FALSE</span>)
+g2 <-<span class="st"> </span>function(y) <span class="kw">f2</span>(y)
+
+<span class="kw">g2</span>(a +<span class="st"> </span>b)
+<span class="co">#> <lazy></span>
+<span class="co">#>   expr: x</span>
+<span class="co">#>   env:  <environment: 0x7fd4dc8465b8></span></code></pre></div>
+<p>Either way, if you evaluate the lazy expression you’ll get the same result:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">a <-<span class="st"> </span><span class="dv">10</span>
+b <-<span class="st"> </span><span class="dv">1</span>
+
+<span class="kw">lazy_eval</span>(<span class="kw">g1</span>(a +<span class="st"> </span>b))
+<span class="co">#> [1] 11</span>
+<span class="kw">lazy_eval</span>(<span class="kw">g2</span>(a +<span class="st"> </span>b))
+<span class="co">#> [1] 11</span></code></pre></div>
+<p>Note that the resolution of chained promises only works with unevaluated objects. This is because R deletes the information about the environment associated with a promise when it has been forced, so that the garbage collector is allowed to remove the environment from memory in case it is no longer used. <code>lazy()</code> will fail with an error in such situations.</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">var <-<span class="st"> </span><span class="dv">0</span>
+
+f3 <-<span class="st"> </span>function(x) {
+  <span class="kw">force</span>(x)
+  <span class="kw">lazy</span>(x)
+}
+
+<span class="kw">f3</span>(var)
+<span class="co">#> Error in lazy(x): Promise has already been forced</span></code></pre></div>
+</div>
+
+
+
+<!-- dynamically load mathjax for compatibility with self-contained -->
+<script>
+  (function () {
+    var script = document.createElement("script");
+    script.type = "text/javascript";
+    script.src  = "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
+    document.getElementsByTagName("head")[0].appendChild(script);
+  })();
+</script>
+
+</body>
+</html>
diff --git a/inst/doc/lazyeval.R b/inst/doc/lazyeval.R
new file mode 100644
index 0000000..e66b48e
--- /dev/null
+++ b/inst/doc/lazyeval.R
@@ -0,0 +1,319 @@
+## ---- include = FALSE----------------------------------------------------
+library(lazyeval)
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+
+## ---- fig.width = 4, fig.height = 2.5------------------------------------
+par(mar = c(4.5, 4.5, 1, 0.5))
+grid <- seq(0, 2 * pi, length = 100)
+plot(grid, sin(grid), type = "l")
+
+## ------------------------------------------------------------------------
+df <- data.frame(x = c(1, 5, 4, 2, 3), y = c(2, 1, 5, 4, 3))
+
+with(df, mean(x))
+subset(df, x == y)
+transform(df, z = x + y)
+
+## ------------------------------------------------------------------------
+my_label <- function(x) deparse(substitute(x))
+my_label(x + y)
+
+## ------------------------------------------------------------------------
+my_label({
+  a + b
+  c + d
+})
+
+## ------------------------------------------------------------------------
+my_label2 <- function(x) my_label(x)
+my_label2(a + b)
+
+## ------------------------------------------------------------------------
+my_label <- function(x) expr_text(x)
+my_label2 <- function(x) my_label(x)
+   
+my_label({
+  a + b
+  c + d
+})
+my_label2(a + b)
+
+## ------------------------------------------------------------------------
+expr_label(x)
+expr_label(a + b + c)
+expr_label(foo({
+  x + y
+}))
+
+## ---- eval = FALSE-------------------------------------------------------
+#  x <- c("a", "b", "c")
+#  my_mean(x)
+#  #> Error: `x` is a not a numeric vector.
+#  my_mean(x == "a")
+#  #> Error: `x == "a"` is not a numeric vector.
+#  my_mean("a")
+#  #> Error: "a" is not a numeric vector.
+
+## ------------------------------------------------------------------------
+f <- ~ x + y + z
+typeof(f)
+attributes(f)
+
+## ------------------------------------------------------------------------
+length(f)
+# The 1st element is always ~
+f[[1]]
+# The 2nd element is the RHS
+f[[2]]
+
+## ------------------------------------------------------------------------
+g <- y ~ x + z
+length(g)
+# The 1st element is still ~
+g[[1]]
+# But now the 2nd element is the LHS
+g[[2]]
+# And the 3rd element is the RHS
+g[[3]]
+
+## ------------------------------------------------------------------------
+f_rhs(f)
+f_lhs(f)
+f_env(f)
+
+f_rhs(g)
+f_lhs(g)
+f_env(g)
+
+## ------------------------------------------------------------------------
+f <- ~ 1 + 2 + 3
+f
+f_eval(f)
+
+## ------------------------------------------------------------------------
+x <- 1
+add_1000 <- function(x) {
+  ~ 1000 + x
+}
+
+add_1000(3)
+f_eval(add_1000(3))
+
+## ------------------------------------------------------------------------
+f_unwrap(add_1000(3))
+
+## ------------------------------------------------------------------------
+y <- 100
+f_eval(~ y)
+f_eval(~ y, data = list(y = 10))
+
+# Can mix variables in environment and data argument
+f_eval(~ x + y, data = list(x = 10))
+# Can even supply functions
+f_eval(~ f(y), data = list(f = function(x) x * 3))
+
+## ------------------------------------------------------------------------
+f_eval(~ mean(cyl), data = mtcars)
+
+## ---- eval = FALSE-------------------------------------------------------
+#  f_eval(~ x, data = mydata)
+
+## ------------------------------------------------------------------------
+mydata <- data.frame(x = 100, y = 1)
+x <- 10
+
+f_eval(~ .env$x, data = mydata)
+f_eval(~ .data$x, data = mydata)
+
+## ---- error = TRUE-------------------------------------------------------
+f_eval(~ .env$z, data = mydata)
+f_eval(~ .data$z, data = mydata)
+
+## ------------------------------------------------------------------------
+df_mean <- function(df, variable) {
+  f_eval(~ mean(uq(variable)), data = df)
+}
+
+df_mean(mtcars, ~ cyl)
+df_mean(mtcars, ~ disp * 0.01638)
+df_mean(mtcars, ~ sqrt(mpg))
+
+## ------------------------------------------------------------------------
+variable <- ~cyl
+f_interp(~ mean(uq(variable)))
+
+variable <- ~ disp * 0.01638
+f_interp(~ mean(uq(variable)))
+
+## ------------------------------------------------------------------------
+f <- ~ mean
+f_interp(~ uq(f)(uq(variable)))
+
+## ------------------------------------------------------------------------
+formula <- y ~ x
+f_interp(~ lm(uq(formula), data = df))
+
+## ------------------------------------------------------------------------
+f_interp(~ lm(uqf(formula), data = df))
+
+## ------------------------------------------------------------------------
+variable <- ~ x
+extra_args <- list(na.rm = TRUE, trim = 0.9)
+f_interp(~ mean(uq(variable), uqs(extra_args)))
+
+## ------------------------------------------------------------------------
+f <- function(x) x + 1
+f_eval(~ f(10), list(f = "a"))
+
+## ------------------------------------------------------------------------
+sieve <- function(df, condition) {
+  rows <- f_eval(condition, df)
+  if (!is.logical(rows)) {
+    stop("`condition` must be logical.", call. = FALSE)
+  }
+  
+  rows[is.na(rows)] <- FALSE
+  df[rows, , drop = FALSE]
+}
+
+df <- data.frame(x = 1:5, y = 5:1)
+sieve(df, ~ x <= 2)
+sieve(df, ~ x == y)
+
+## ---- eval = FALSE-------------------------------------------------------
+#  sieve(march, ~ x > 100)
+#  sieve(april, ~ x > 50)
+#  sieve(june, ~ x > 45)
+#  sieve(july, ~ x > 17)
+
+## ------------------------------------------------------------------------
+threshold_x <- function(df, threshold) {
+  sieve(df, ~ x > threshold)
+}
+threshold_x(df, 3)
+
+## ---- error = TRUE-------------------------------------------------------
+rm(x)
+df2 <- data.frame(y = 5:1)
+
+# Throws an error
+threshold_x(df2, 3)
+
+# Silently gives the incorrect result!
+x <- 5
+threshold_x(df2, 3)
+
+## ------------------------------------------------------------------------
+df3 <- data.frame(x = 1:5, y = 5:1, threshold = 4)
+threshold_x(df3, 3)
+
+## ---- error = TRUE-------------------------------------------------------
+threshold_x <- function(df, threshold) {
+  sieve(df, ~ .data$x > .env$threshold)
+}
+
+threshold_x(df2, 3)
+threshold_x(df3, 3)
+
+## ------------------------------------------------------------------------
+threshold <- function(df, variable, threshold) {
+  stopifnot(is.character(variable), length(variable) == 1)
+  
+  sieve(df, ~ .data[[.env$variable]] > .env$threshold)
+}
+threshold(df, "x", 4)
+
+## ------------------------------------------------------------------------
+threshold <- function(df, variable = ~x, threshold = 0) {
+  sieve(df, ~ uq(variable) > .env$threshold)
+}
+
+threshold(df, ~ x, 4)
+threshold(df, ~ abs(x - y), 2)
+
+## ------------------------------------------------------------------------
+x <- 3
+threshold(df, ~ .data$x - .env$x, 0)
+
+## ------------------------------------------------------------------------
+mogrify <- function(`_df`, ...) {
+  args <- list(...)
+  
+  for (nm in names(args)) {
+    `_df`[[nm]] <- f_eval(args[[nm]], `_df`)
+  }
+  
+  `_df`
+}
+
+## ------------------------------------------------------------------------
+df <- data.frame(x = 1:5, y = sample(5))
+mogrify(df, z = ~ x + y, z2 = ~ z * 2)
+
+## ------------------------------------------------------------------------
+add_variable <- function(df, name, expr) {
+  do.call("mogrify", c(list(df), setNames(list(expr), name)))
+}
+add_variable(df, "z", ~ x + y)
+
+## ------------------------------------------------------------------------
+f_list("x" ~ y, z = ~z)
+
+## ------------------------------------------------------------------------
+mogrify <- function(`_df`, ...) {
+  args <- f_list(...)
+  
+  for (nm in names(args)) {
+    `_df`[[nm]] <- f_eval(args[[nm]], `_df`)
+  }
+  
+  `_df`
+}
+
+## ------------------------------------------------------------------------
+add_variable <- function(df, name, expr) {
+  mogrify(df, name ~ uq(expr))
+}
+add_variable(df, "z", ~ x + y)
+
+## ------------------------------------------------------------------------
+sieve_ <- function(df, condition) {
+  rows <- f_eval(condition, df)
+  if (!is.logical(rows)) {
+    stop("`condition` must be logical.", call. = FALSE)
+  }
+  
+  rows[is.na(rows)] <- FALSE
+  df[rows, , drop = FALSE]
+}
+
+## ------------------------------------------------------------------------
+sieve <- function(df, expr) {
+  sieve_(df, f_capture(expr))
+}
+sieve(df, x == 1)
+
+## ------------------------------------------------------------------------
+scramble <- function(df) {
+  df[sample(nrow(df)), , drop = FALSE]
+}
+subscramble <- function(df, expr) {
+  scramble(sieve(df, expr))
+}
+subscramble(df, x < 4)
+
+## ------------------------------------------------------------------------
+mogrify_ <- function(`_df`, args) {
+  args <- as_f_list(args)
+  
+  for (nm in names(args)) {
+    `_df`[[nm]] <- f_eval(args[[nm]], `_df`)
+  }
+  
+  `_df`
+}
+
+mogrify <- function(`_df`, ...) {
+  mogrify_(`_df`, dots_capture(...))
+}
+
diff --git a/inst/doc/lazyeval.Rmd b/inst/doc/lazyeval.Rmd
new file mode 100644
index 0000000..c672fe3
--- /dev/null
+++ b/inst/doc/lazyeval.Rmd
@@ -0,0 +1,617 @@
+---
+title: "Non-standard evaluation"
+author: "Hadley Wickham"
+date: "`r Sys.Date()`"
+output: rmarkdown::html_vignette
+vignette: >
+  %\VignetteIndexEntry{Non-standard evaluation}
+  %\VignetteEngine{knitr::rmarkdown}
+  %\VignetteEncoding{UTF-8}
+---
+
+```{r, include = FALSE}
+library(lazyeval)
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+```
+
+This document describes lazyeval, a package that provides principled tools to perform non-standard evaluation (NSE) in R. You should read this vignette if you want to program with packages like dplyr and ggplot2[^1], or you want a principled way of working with delayed expressions in your own package. As the name suggests, non-standard evaluation breaks away from the standard evaluation (SE) rules in order to do something special. There are three common uses of NSE:
+
+1.  __Labelling__ enhances plots and tables by using the expressions
+    supplied to a function, rather than their values. For example, note the
+    axis labels in this plot:
+
+    ```{r, fig.width = 4, fig.height = 2.5}
+    par(mar = c(4.5, 4.5, 1, 0.5))
+    grid <- seq(0, 2 * pi, length = 100)
+    plot(grid, sin(grid), type = "l")
+    ```
+
+1.  __Non-standard scoping__ looks for objects in places other than the current
+    environment. For example, base R has `with()`, `subset()`, and `transform()` 
+    that look for objects in a data frame (or list) before the current 
+    environment:
+
+    ```{r}
+    df <- data.frame(x = c(1, 5, 4, 2, 3), y = c(2, 1, 5, 4, 3))
+    
+    with(df, mean(x))
+    subset(df, x == y)
+    transform(df, z = x + y)
+    ```
+
+1.  __Metaprogramming__ is a catch-all term that covers all other uses of 
+    NSE (such as in `bquote()` and `library()`). Metaprogramming is so called 
+    because it involves computing on the unevaluated code in some way.
+
+This document is broadly organised according to the three types of non-standard evaluation described above. The main difference is that after [labelling], we'll take a detour to learn more about [formulas]. You're probably familiar with formulas from linear models (e.g. `lm(mpg ~ displ, data = mtcars)`) but formulas are more than just a tool for modelling: they are a general way of capturing an unevaluated expression. 
+
+The approaches recommended here are quite different to my previous generation of recommendations. I am fairly confident these new approaches are correct, and will not have to change substantially again. The current tools make it easy to solve a number of practical problems that were previously challenging and are rooted in [long-standing theory](http://repository.readscheme.org/ftp/papers/pepm99/bawden.pdf).
+
+[^1]: Currently neither ggplot2 nor dplyr actually use these tools since I've only just figured it out. But I'll be working hard to make sure all my packages are consistent in the near future.
+
+## Labelling
+
+In base R, the classic way to turn an argument into a label is to use `deparse(substitute(x))`:
+
+```{r}
+my_label <- function(x) deparse(substitute(x))
+my_label(x + y)
+```
+
+There are two potential problems with this approach:
+
+1.  For long some expressions, `deparse()` generates a character vector with 
+    length > 1:
+    
+    ```{r}
+    my_label({
+      a + b
+      c + d
+    })
+    ```
+
+1.  `substitute()` only looks one level up, so you lose the original label if 
+    the function isn't called directly:
+    
+    ```{r}
+    my_label2 <- function(x) my_label(x)
+    my_label2(a + b)
+    ```
+
+Both of these problems are resolved by `lazyeval::expr_text()`:
+
+```{r}
+my_label <- function(x) expr_text(x)
+my_label2 <- function(x) my_label(x)
+   
+my_label({
+  a + b
+  c + d
+})
+my_label2(a + b)
+```
+
+There are two variations on the theme of `expr_text()`:
+
+*   `expr_find()` find the underlying expression. It works similarly to 
+    `substitute()` but will follow a chain of promises back up to the original
+    expression. This is often useful for [metaprogramming].
+  
+*   `expr_label()` is a customised version of `expr_text()` that produces 
+    labels designed to be used in messages to the user:
+
+    ```{r}
+    expr_label(x)
+    expr_label(a + b + c)
+    expr_label(foo({
+      x + y
+    }))
+    ```
+
+### Exercises
+
+1.  `plot()` uses `deparse(substitute(x))` to generate labels for the x and y
+    axes. Can you generate input that causes it to display bad labels?
+    Write your own wrapper around `plot()` that uses `expr_label()` to compute
+    `xlim` and `ylim`.
+    
+1.  Create a simple implementation of `mean()` that stops with an informative
+    error message if the argument is not numeric:
+    
+    ```{r, eval = FALSE}
+    x <- c("a", "b", "c")
+    my_mean(x)
+    #> Error: `x` is a not a numeric vector.
+    my_mean(x == "a")
+    #> Error: `x == "a"` is not a numeric vector.
+    my_mean("a")
+    #> Error: "a" is not a numeric vector.
+    ```
+
+1.  Read the source code for `expr_text()`. How does it work? What additional
+    arguments to `deparse()` does it use?
+
+## Formulas
+
+Non-standard scoping is probably the most useful NSE tool, but before we can talk about a solid approach, we need to take a detour to talk about formulas. Formulas are a familiar tool from linear models, but their utility is not limited to models. In fact, formulas are a powerful, general purpose tool, because a formula captures two things: 
+
+1. An unevaluated expression.
+1. The context (environment) in which the expression was created.
+
+`~` is a single character that allows you to say: "I want to capture the meaning of this code, without evaluating it right away". For that reason, the formula can be thought of as a "quoting" operator.
+
+### Definition of a formula
+
+Technically, a formula is a "language" object (i.e. an unevaluated expression) with a class of "formula" and an attribute that stores the environment:
+
+```{r}
+f <- ~ x + y + z
+typeof(f)
+attributes(f)
+```
+
+The structure of the underlying object is slightly different depending on whether you have a one-sided or two-sided formula:
+
+*   One-sided formulas have length two:
+
+    ```{r}
+    length(f)
+    # The 1st element is always ~
+    f[[1]]
+    # The 2nd element is the RHS
+    f[[2]]
+    ```
+
+*   Two-sided formulas have length three:
+
+    ```{r}
+    g <- y ~ x + z
+    length(g)
+    # The 1st element is still ~
+    g[[1]]
+    # But now the 2nd element is the LHS
+    g[[2]]
+    # And the 3rd element is the RHS
+    g[[3]]
+    ```
+
+To abstract away these differences, lazyeval provides `f_rhs()` and `f_lhs()` to access either side of the formula, and `f_env()` to access its environment:
+
+```{r}
+f_rhs(f)
+f_lhs(f)
+f_env(f)
+
+f_rhs(g)
+f_lhs(g)
+f_env(g)
+```
+
+### Evaluating a formula
+
+A formula captures delays the evaluation of an expression so you can later evaluate it with `f_eval()`:
+
+```{r}
+f <- ~ 1 + 2 + 3
+f
+f_eval(f)
+```
+
+This allows you to use a formula as a robust way of delaying evaluation, cleanly separating the creation of the formula from its evaluation. Because formulas capture the code and context, you get the correct result even when a formula is created and evaluated in different places. In the following example, note that the value of `x` inside `add_1000()` is used:
+
+```{r}
+x <- 1
+add_1000 <- function(x) {
+  ~ 1000 + x
+}
+
+add_1000(3)
+f_eval(add_1000(3))
+```
+
+It can be hard to see what's going on when looking at a formula because important values are stored in the environment, which is largely opaque. You can use `f_unwrap()` to replace names with their corresponding values:
+
+```{r}
+f_unwrap(add_1000(3))
+```
+
+### Non-standard scoping
+
+`f_eval()` has an optional second argument: a named list (or data frame) that overrides values found in the formula's environment. 
+
+```{r}
+y <- 100
+f_eval(~ y)
+f_eval(~ y, data = list(y = 10))
+
+# Can mix variables in environment and data argument
+f_eval(~ x + y, data = list(x = 10))
+# Can even supply functions
+f_eval(~ f(y), data = list(f = function(x) x * 3))
+```
+
+This makes it very easy to implement non-standard scoping:
+
+```{r}
+f_eval(~ mean(cyl), data = mtcars)
+```
+
+One challenge with non-standard scoping is that we've introduced some ambiguity. For example, in the code below does `x` come from `mydata` or the environment?
+
+```{r, eval = FALSE}
+f_eval(~ x, data = mydata)
+```
+
+You can't tell without knowing whether or not `mydata` has a variable called `x`. To overcome this problem, `f_eval()` provides two pronouns:
+
+* `.data` is bound to the data frame.
+* `.env` is bound to the formula environment.
+
+They both start with `.` to minimise the chances of clashing with existing variables.
+
+With these pronouns we can rewrite the previous formula to remove the ambiguity:
+
+```{r}
+mydata <- data.frame(x = 100, y = 1)
+x <- 10
+
+f_eval(~ .env$x, data = mydata)
+f_eval(~ .data$x, data = mydata)
+```
+
+If the variable or object doesn't exist, you'll get an informative error:
+
+```{r, error = TRUE}
+f_eval(~ .env$z, data = mydata)
+f_eval(~ .data$z, data = mydata)
+```
+
+### Unquoting
+
+`f_eval()` has one more useful trick up its sleeve: unquoting. Unquoting allows you to write functions where the user supplies part of the formula. For example, the following function allows you to compute the mean of any column (or any function of a column):
+
+```{r}
+df_mean <- function(df, variable) {
+  f_eval(~ mean(uq(variable)), data = df)
+}
+
+df_mean(mtcars, ~ cyl)
+df_mean(mtcars, ~ disp * 0.01638)
+df_mean(mtcars, ~ sqrt(mpg))
+```
+
+To see how this works, we can use `f_interp()` which `f_eval()` calls internally (you shouldn't call it in your own code, but it's useful for debugging). The key is `uq()`: `uq()` evaluates its first (and only) argument and inserts the value into the formula:
+    
+```{r}
+variable <- ~cyl
+f_interp(~ mean(uq(variable)))
+
+variable <- ~ disp * 0.01638
+f_interp(~ mean(uq(variable)))
+```
+
+Unquoting allows you to create code "templates", where you write most of the expression, while still allowing the user to control important components. You can even use `uq()` to change the function being called:
+
+```{r}
+f <- ~ mean
+f_interp(~ uq(f)(uq(variable)))
+```
+
+Note that `uq()` only takes the RHS of a formula, which makes it difficult to insert literal formulas into a call:
+
+```{r}
+formula <- y ~ x
+f_interp(~ lm(uq(formula), data = df))
+```
+
+You can instead use `uqf()` which uses the whole formula, not just the RHS:
+
+```{r}
+f_interp(~ lm(uqf(formula), data = df))
+```
+
+Unquoting is powerful, but it only allows you to modify a single argument: it doesn't allow you to add an arbitrary number of arguments. To do that, you'll need "unquote-splice", or `uqs()`. The first (and only) argument to `uqs()` should be a list of arguments to be spliced into the call:
+
+```{r}
+variable <- ~ x
+extra_args <- list(na.rm = TRUE, trim = 0.9)
+f_interp(~ mean(uq(variable), uqs(extra_args)))
+```
+
+### Exercises
+
+1.  Create a wrapper around `lm()` that allows the user to supply the 
+    response and predictors as two separate formulas.
+    
+1.  Compare and contrast `f_eval()` with `with()`.
+
+1.  Why does this code work even though `f` is defined in two places? (And
+    one of them is not a function).
+
+    ```{r}
+    f <- function(x) x + 1
+    f_eval(~ f(10), list(f = "a"))
+    ```
+
+## Non-standard scoping
+
+Non-standard scoping (NSS) is an important part of R because it makes it easy to write functions tailored for interactive data exploration. These functions require less typing, at the cost of some ambiguity and "magic". This is a good trade-off for interactive data exploration because you want to get ideas out of your head and into the computer as quickly as possible. If a function does make a bad guess, you'll spot it quickly because you're working interactively.
+
+There are three challenges to implementing non-standard scoping:
+
+1.  You must correctly delay the evaluation of a function argument, capturing 
+    both the computation (the expression), and the context (the environment).
+    I recommend making this explicit by requiring the user to "quote" any NSS
+    arguments with `~`, and then evaluating explicit with `f_eval()`.
+  
+1.  When writing functions that use NSS-functions, you need some way to
+    avoid the automatic lookup and be explicit about where objects should be
+    found. `f_eval()` solves this problem with the `.data.` and `.env` 
+    pronouns.
+
+1.  You need some way to allow the user to supply parts of a formula. 
+    `f_eval()` solves this with unquoting.
+
+To illustrate these challenges, I will implement a `sieve()` function that works similarly to `base::subset()` or `dplyr::filter()`. The goal of `sieve()` is to make it easy to select observations that match criteria defined by a logical expression. `sieve()` has three advantages over `[`:
+
+1.  It is much more compact when the condition uses many variables, because 
+    you don't need to repeat the name of the data frame many times.
+
+1.  It drops rows where the condition evaluates to `NA`, rather than filling 
+    them with `NA`s.
+    
+1.  It always returns a data frame.
+
+The implementation of `sieve()` is straightforward. First we use `f_eval()` to perform NSS. Then we then check that we have a logical vector, replace `NA`s with `FALSE`, and subset with `[`.
+
+```{R}
+sieve <- function(df, condition) {
+  rows <- f_eval(condition, df)
+  if (!is.logical(rows)) {
+    stop("`condition` must be logical.", call. = FALSE)
+  }
+  
+  rows[is.na(rows)] <- FALSE
+  df[rows, , drop = FALSE]
+}
+
+df <- data.frame(x = 1:5, y = 5:1)
+sieve(df, ~ x <= 2)
+sieve(df, ~ x == y)
+```
+
+### Programming with `sieve()`
+
+Imagine that you've written some code that looks like this:
+
+```{r, eval = FALSE}
+sieve(march, ~ x > 100)
+sieve(april, ~ x > 50)
+sieve(june, ~ x > 45)
+sieve(july, ~ x > 17)
+```
+
+(This is a contrived example, but it illustrates all of the important issues you'll need to consider when writing more useful functions.)
+
+Instead of continuing to copy-and-paste your code, you decide to wrap up the common behaviour in a function: 
+
+```{r}
+threshold_x <- function(df, threshold) {
+  sieve(df, ~ x > threshold)
+}
+threshold_x(df, 3)
+```
+
+There are two ways that this function might fail:
+
+1.  The data frame might not have a variable called `x`. This will fail unless
+    there's a variable called `x` hanging around in the global environment:
+    
+    ```{r, error = TRUE}
+    rm(x)
+    df2 <- data.frame(y = 5:1)
+    
+    # Throws an error
+    threshold_x(df2, 3)
+    
+    # Silently gives the incorrect result!
+    x <- 5
+    threshold_x(df2, 3)
+    ```
+    
+1.  The data frame might have a variable called `threshold`:
+
+    ```{r}
+    df3 <- data.frame(x = 1:5, y = 5:1, threshold = 4)
+    threshold_x(df3, 3)
+    ```
+
+These failures are partiuclarly pernicious because instead of throwing an error they silently produce the wrong answer. Both failures arise because `f_eval()` introduces ambiguity by looking in two places for each name: the supplied data and formula environment. 
+
+To make `threshold_x()` more reliable, we need to be more explicit by using the `.data` and `.env` pronouns:
+
+```{r, error = TRUE}
+threshold_x <- function(df, threshold) {
+  sieve(df, ~ .data$x > .env$threshold)
+}
+
+threshold_x(df2, 3)
+threshold_x(df3, 3)
+```
+
+Here `.env` is bound to the environment where `~` is evaluated, namely the inside of `threshold_x()`.
+
+### Adding arguments
+
+The `threshold_x()` function is not very useful because it's bound to a specific variable. It would be more powerful if we could vary both the threshold and the variable it applies to. We can do that by taking an additional argument to specify which variable to use. 
+
+One simple approach is to use a string and `[[`:
+
+```{r}
+threshold <- function(df, variable, threshold) {
+  stopifnot(is.character(variable), length(variable) == 1)
+  
+  sieve(df, ~ .data[[.env$variable]] > .env$threshold)
+}
+threshold(df, "x", 4)
+```
+
+This is a simple and robust solution, but only allows us to use an existing variable, not an arbitrary expression like `sqrt(x)`.
+
+A more general solution is to allow the user to supply a formula, and use unquoting:
+
+```{r}
+threshold <- function(df, variable = ~x, threshold = 0) {
+  sieve(df, ~ uq(variable) > .env$threshold)
+}
+
+threshold(df, ~ x, 4)
+threshold(df, ~ abs(x - y), 2)
+```
+
+In this case, it's the responsibility of the user to ensure the `variable` is specified unambiguously. `f_eval()` is designed so that `.data` and `.env` work even when evaluated inside of `uq()`:
+
+```{r}
+x <- 3
+threshold(df, ~ .data$x - .env$x, 0)
+```
+
+### Dot-dot-dot
+
+There is one more tool that you might find useful for functions that take `...`. For example, the code below implements a function similar to `dplyr::mutate()` or `base::transform()`.
+
+```{r}
+mogrify <- function(`_df`, ...) {
+  args <- list(...)
+  
+  for (nm in names(args)) {
+    `_df`[[nm]] <- f_eval(args[[nm]], `_df`)
+  }
+  
+  `_df`
+}
+```
+
+(NB: the first argument is a non-syntactic name (i.e. it requires quoting with `` ` ``) so it doesn't accidentally match one of the names of the new variables.)
+
+`transmogrifty()` makes it easy to add new variables to a data frame:
+
+```{r}
+df <- data.frame(x = 1:5, y = sample(5))
+mogrify(df, z = ~ x + y, z2 = ~ z * 2)
+```
+
+One problem with this implementation is that it's hard to specify the names of the generated variables. Imagine you want a function where the name and expression are in separate variables. This is awkward because the variable name is supplied as an argument name to `mogrify()`:
+
+```{r}
+add_variable <- function(df, name, expr) {
+  do.call("mogrify", c(list(df), setNames(list(expr), name)))
+}
+add_variable(df, "z", ~ x + y)
+```
+
+Lazyeval provides the `f_list()` function to make writing this sort of function a little easier. It takes a list of formulas and evaluates the LHS of each formula (if present) to rename the elements:
+
+```{r}
+f_list("x" ~ y, z = ~z)
+```
+
+If we tweak `mogrify()` to use `f_list()` instead of `list()`:
+
+```{r}
+mogrify <- function(`_df`, ...) {
+  args <- f_list(...)
+  
+  for (nm in names(args)) {
+    `_df`[[nm]] <- f_eval(args[[nm]], `_df`)
+  }
+  
+  `_df`
+}
+```
+
+`add_new()` becomes much simpler:
+
+```{r}
+add_variable <- function(df, name, expr) {
+  mogrify(df, name ~ uq(expr))
+}
+add_variable(df, "z", ~ x + y)
+```
+
+### Exercises
+
+1.  Write a function that selects all rows of `df` where `variable` is 
+    greater than its mean. Make the function more general by allowing the
+    user to specify a function to use instead of `mean()` (e.g. `median()`).
+
+1.  Create a version of `mogrify()` where the first argument is `x`?
+    What happens if you try to create a new variable called `x`?
+
+## Non-standard evaluation
+
+In some situations you might want to eliminate the formula altogether, and allow the user to type expressions directly. I was once much enamoured with this approach (witness ggplot2, dplyr, ...). However, I now think that it should be used sparingly because explict quoting with `~` leads to simpler code, and makes it more clear to the user that something special is going on.
+
+That said, lazyeval does allow you to eliminate the `~` if you really want to. In this case, I recommend having both a NSE and SE version of the function. The SE version, which takes formuals, should have suffix `_`:
+
+```{r}
+sieve_ <- function(df, condition) {
+  rows <- f_eval(condition, df)
+  if (!is.logical(rows)) {
+    stop("`condition` must be logical.", call. = FALSE)
+  }
+  
+  rows[is.na(rows)] <- FALSE
+  df[rows, , drop = FALSE]
+}
+```
+
+Then create the NSE version which doesn't need the explicit formula. The key is the use of `f_capture()` which takes an unevaluated argument (a promise) and captures it as a formula:
+
+```{r}
+sieve <- function(df, expr) {
+  sieve_(df, f_capture(expr))
+}
+sieve(df, x == 1)
+```
+
+If you're familiar with `substitute()` you might expect the same drawbacks to apply. However, `f_capture()` is smart enough to follow a chain of promises back to the original value, so, for example, this code works fine:
+
+```{r}
+scramble <- function(df) {
+  df[sample(nrow(df)), , drop = FALSE]
+}
+subscramble <- function(df, expr) {
+  scramble(sieve(df, expr))
+}
+subscramble(df, x < 4)
+```
+
+### Dot-dot-dot
+
+If you want a `...` function that doesn't require formulas, I recommend that the SE version take a list of arguments, and the NSE version uses `dots_capture()` to capture multiple arguments as a list of formulas.
+
+```{r}
+mogrify_ <- function(`_df`, args) {
+  args <- as_f_list(args)
+  
+  for (nm in names(args)) {
+    `_df`[[nm]] <- f_eval(args[[nm]], `_df`)
+  }
+  
+  `_df`
+}
+
+mogrify <- function(`_df`, ...) {
+  mogrify_(`_df`, dots_capture(...))
+}
+```
+
+### Exercises
+
+1.  Recreate `subscramble()` using `base::subset()` instead of `sieve()`.
+    Why does it fail?
+
+## Metaprogramming
+
+The final use of non-standard evaluation is to do metaprogramming. This is a catch-all term that encompasses any function that does computation on an unevaluated expression. You can learn about metaprogrgramming in <http://adv-r.had.co.nz/Expressions.html>, particularly <http://adv-r.had.co.nz/Expressions.html#ast-funs>. Over time, the goal is to move all useful metaprogramming helper functions into this package, and discuss metaprogramming more here.
diff --git a/inst/doc/lazyeval.html b/inst/doc/lazyeval.html
new file mode 100644
index 0000000..55134da
--- /dev/null
+++ b/inst/doc/lazyeval.html
@@ -0,0 +1,606 @@
+<!DOCTYPE html>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+
+<meta charset="utf-8">
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="pandoc" />
+
+<meta name="viewport" content="width=device-width, initial-scale=1">
+
+<meta name="author" content="Hadley Wickham" />
+
+<meta name="date" content="2016-06-10" />
+
+<title>Non-standard evaluation</title>
+
+
+
+<style type="text/css">code{white-space: pre;}</style>
+<style type="text/css">
+div.sourceCode { overflow-x: auto; }
+table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
+  margin: 0; padding: 0; vertical-align: baseline; border: none; }
+table.sourceCode { width: 100%; line-height: 100%; }
+td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
+td.sourceCode { padding-left: 5px; }
+code > span.kw { color: #007020; font-weight: bold; } /* Keyword */
+code > span.dt { color: #902000; } /* DataType */
+code > span.dv { color: #40a070; } /* DecVal */
+code > span.bn { color: #40a070; } /* BaseN */
+code > span.fl { color: #40a070; } /* Float */
+code > span.ch { color: #4070a0; } /* Char */
+code > span.st { color: #4070a0; } /* String */
+code > span.co { color: #60a0b0; font-style: italic; } /* Comment */
+code > span.ot { color: #007020; } /* Other */
+code > span.al { color: #ff0000; font-weight: bold; } /* Alert */
+code > span.fu { color: #06287e; } /* Function */
+code > span.er { color: #ff0000; font-weight: bold; } /* Error */
+code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
+code > span.cn { color: #880000; } /* Constant */
+code > span.sc { color: #4070a0; } /* SpecialChar */
+code > span.vs { color: #4070a0; } /* VerbatimString */
+code > span.ss { color: #bb6688; } /* SpecialString */
+code > span.im { } /* Import */
+code > span.va { color: #19177c; } /* Variable */
+code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
+code > span.op { color: #666666; } /* Operator */
+code > span.bu { } /* BuiltIn */
+code > span.ex { } /* Extension */
+code > span.pp { color: #bc7a00; } /* Preprocessor */
+code > span.at { color: #7d9029; } /* Attribute */
+code > span.do { color: #ba2121; font-style: italic; } /* Documentation */
+code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
+code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
+code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
+</style>
+
+
+
+<link href="data:text/css;charset=utf-8,body%20%7B%0Abackground%2Dcolor%3A%20%23fff%3B%0Amargin%3A%201em%20auto%3B%0Amax%2Dwidth%3A%20700px%3B%0Aoverflow%3A%20visible%3B%0Apadding%2Dleft%3A%202em%3B%0Apadding%2Dright%3A%202em%3B%0Afont%2Dfamily%3A%20%22Open%20Sans%22%2C%20%22Helvetica%20Neue%22%2C%20Helvetica%2C%20Arial%2C%20sans%2Dserif%3B%0Afont%2Dsize%3A%2014px%3B%0Aline%2Dheight%3A%201%2E35%3B%0A%7D%0A%23header%20%7B%0Atext%2Dalign%3A%20center%3B%0A%7D%0A%23TOC%20%7B%0Aclear%3A%20bot [...]
+
+</head>
+
+<body>
+
+
+
+
+<h1 class="title toc-ignore">Non-standard evaluation</h1>
+<h4 class="author"><em>Hadley Wickham</em></h4>
+<h4 class="date"><em>2016-06-10</em></h4>
+
+
+
+<p>This document describes lazyeval, a package that provides principled tools to perform non-standard evaluation (NSE) in R. You should read this vignette if you want to program with packages like dplyr and ggplot2<a href="#fn1" class="footnoteRef" id="fnref1"><sup>1</sup></a>, or you want a principled way of working with delayed expressions in your own package. As the name suggests, non-standard evaluation breaks away from the standard evaluation (SE) rules in order to do something spec [...]
+<ol style="list-style-type: decimal">
+<li><p><strong>Labelling</strong> enhances plots and tables by using the expressions supplied to a function, rather than their values. For example, note the axis labels in this plot:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">par</span>(<span class="dt">mar =</span> <span class="kw">c</span>(<span class="fl">4.5</span>, <span class="fl">4.5</span>, <span class="dv">1</span>, <span class="fl">0.5</span>))
+grid <-<span class="st"> </span><span class="kw">seq</span>(<span class="dv">0</span>, <span class="dv">2</span> *<span class="st"> </span>pi, <span class="dt">length =</span> <span class="dv">100</span>)
+<span class="kw">plot</span>(grid, <span class="kw">sin</span>(grid), <span class="dt">type =</span> <span class="st">"l"</span>)</code></pre></div>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYAAAADwCAYAAAAJkrPKAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<li><p><strong>Non-standard scoping</strong> looks for objects in places other than the current environment. For example, base R has <code>with()</code>, <code>subset()</code>, and <code>transform()</code> that look for objects in a data frame (or list) before the current environment:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">df <-<span class="st"> </span><span class="kw">data.frame</span>(<span class="dt">x =</span> <span class="kw">c</span>(<span class="dv">1</span>, <span class="dv">5</span>, <span class="dv">4</span>, <span class="dv">2</span>, <span class="dv">3</span>), <span class="dt">y =</span> <span class="kw">c</span>(<span class="dv">2</span>, <span class="dv">1</span>, <span class="dv">5</span>, <span class="dv">4</sp [...]
+
+<span class="kw">with</span>(df, <span class="kw">mean</span>(x))
+<span class="co">#> [1] 3</span>
+<span class="kw">subset</span>(df, x ==<span class="st"> </span>y)
+<span class="co">#>   x y</span>
+<span class="co">#> 5 3 3</span>
+<span class="kw">transform</span>(df, <span class="dt">z =</span> x +<span class="st"> </span>y)
+<span class="co">#>   x y z</span>
+<span class="co">#> 1 1 2 3</span>
+<span class="co">#> 2 5 1 6</span>
+<span class="co">#> 3 4 5 9</span>
+<span class="co">#> 4 2 4 6</span>
+<span class="co">#> 5 3 3 6</span></code></pre></div></li>
+<li><p><strong>Metaprogramming</strong> is a catch-all term that covers all other uses of NSE (such as in <code>bquote()</code> and <code>library()</code>). Metaprogramming is so called because it involves computing on the unevaluated code in some way.</p></li>
+</ol>
+<p>This document is broadly organised according to the three types of non-standard evaluation described above. The main difference is that after <a href="#labelling">labelling</a>, we’ll take a detour to learn more about <a href="#formulas">formulas</a>. You’re probably familiar with formulas from linear models (e.g. <code>lm(mpg ~ displ, data = mtcars)</code>) but formulas are more than just a tool for modelling: they are a general way of capturing an unevaluated expression.</p>
+<p>The approaches recommended here are quite different to my previous generation of recommendations. I am fairly confident these new approaches are correct, and will not have to change substantially again. The current tools make it easy to solve a number of practical problems that were previously challenging and are rooted in <a href="http://repository.readscheme.org/ftp/papers/pepm99/bawden.pdf">long-standing theory</a>.</p>
+<div id="labelling" class="section level2">
+<h2>Labelling</h2>
+<p>In base R, the classic way to turn an argument into a label is to use <code>deparse(substitute(x))</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">my_label <-<span class="st"> </span>function(x) <span class="kw">deparse</span>(<span class="kw">substitute</span>(x))
+<span class="kw">my_label</span>(x +<span class="st"> </span>y)
+<span class="co">#> [1] "x + y"</span></code></pre></div>
+<p>There are two potential problems with this approach:</p>
+<ol style="list-style-type: decimal">
+<li><p>For long some expressions, <code>deparse()</code> generates a character vector with length > 1:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">my_label</span>({
+  a +<span class="st"> </span>b
+  c +<span class="st"> </span>d
+})
+<span class="co">#> [1] "{"         "    a + b" "    c + d" "}"</span></code></pre></div></li>
+<li><p><code>substitute()</code> only looks one level up, so you lose the original label if the function isn’t called directly:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">my_label2 <-<span class="st"> </span>function(x) <span class="kw">my_label</span>(x)
+<span class="kw">my_label2</span>(a +<span class="st"> </span>b)
+<span class="co">#> [1] "x"</span></code></pre></div></li>
+</ol>
+<p>Both of these problems are resolved by <code>lazyeval::expr_text()</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">my_label <-<span class="st"> </span>function(x) <span class="kw">expr_text</span>(x)
+my_label2 <-<span class="st"> </span>function(x) <span class="kw">my_label</span>(x)
+   
+<span class="kw">my_label</span>({
+  a +<span class="st"> </span>b
+  c +<span class="st"> </span>d
+})
+<span class="co">#> [1] "{\n    a + b\n    c + d\n}"</span>
+<span class="kw">my_label2</span>(a +<span class="st"> </span>b)
+<span class="co">#> [1] "a + b"</span></code></pre></div>
+<p>There are two variations on the theme of <code>expr_text()</code>:</p>
+<ul>
+<li><p><code>expr_find()</code> find the underlying expression. It works similarly to <code>substitute()</code> but will follow a chain of promises back up to the original expression. This is often useful for <a href="#metaprogramming">metaprogramming</a>.</p></li>
+<li><p><code>expr_label()</code> is a customised version of <code>expr_text()</code> that produces labels designed to be used in messages to the user:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">expr_label</span>(x)
+<span class="co">#> [1] "`x`"</span>
+<span class="kw">expr_label</span>(a +<span class="st"> </span>b +<span class="st"> </span>c)
+<span class="co">#> [1] "`a + b + c`"</span>
+<span class="kw">expr_label</span>(<span class="kw">foo</span>({
+  x +<span class="st"> </span>y
+}))
+<span class="co">#> [1] "`foo(...)`"</span></code></pre></div></li>
+</ul>
+<div id="exercises" class="section level3">
+<h3>Exercises</h3>
+<ol style="list-style-type: decimal">
+<li><p><code>plot()</code> uses <code>deparse(substitute(x))</code> to generate labels for the x and y axes. Can you generate input that causes it to display bad labels? Write your own wrapper around <code>plot()</code> that uses <code>expr_label()</code> to compute <code>xlim</code> and <code>ylim</code>.</p></li>
+<li><p>Create a simple implementation of <code>mean()</code> that stops with an informative error message if the argument is not numeric:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">x <-<span class="st"> </span><span class="kw">c</span>(<span class="st">"a"</span>, <span class="st">"b"</span>, <span class="st">"c"</span>)
+<span class="kw">my_mean</span>(x)
+<span class="co">#> Error: `x` is a not a numeric vector.</span>
+<span class="kw">my_mean</span>(x ==<span class="st"> "a"</span>)
+<span class="co">#> Error: `x == "a"` is not a numeric vector.</span>
+<span class="kw">my_mean</span>(<span class="st">"a"</span>)
+<span class="co">#> Error: "a" is not a numeric vector.</span></code></pre></div></li>
+<li><p>Read the source code for <code>expr_text()</code>. How does it work? What additional arguments to <code>deparse()</code> does it use?</p></li>
+</ol>
+</div>
+</div>
+<div id="formulas" class="section level2">
+<h2>Formulas</h2>
+<p>Non-standard scoping is probably the most useful NSE tool, but before we can talk about a solid approach, we need to take a detour to talk about formulas. Formulas are a familiar tool from linear models, but their utility is not limited to models. In fact, formulas are a powerful, general purpose tool, because a formula captures two things:</p>
+<ol style="list-style-type: decimal">
+<li>An unevaluated expression.</li>
+<li>The context (environment) in which the expression was created.</li>
+</ol>
+<p><code>~</code> is a single character that allows you to say: “I want to capture the meaning of this code, without evaluating it right away”. For that reason, the formula can be thought of as a “quoting” operator.</p>
+<div id="definition-of-a-formula" class="section level3">
+<h3>Definition of a formula</h3>
+<p>Technically, a formula is a “language” object (i.e. an unevaluated expression) with a class of “formula” and an attribute that stores the environment:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">f <-<span class="st"> </span><span class="er">~</span><span class="st"> </span>x +<span class="st"> </span>y +<span class="st"> </span>z
+<span class="kw">typeof</span>(f)
+<span class="co">#> [1] "language"</span>
+<span class="kw">attributes</span>(f)
+<span class="co">#> $class</span>
+<span class="co">#> [1] "formula"</span>
+<span class="co">#> </span>
+<span class="co">#> $.Environment</span>
+<span class="co">#> <environment: R_GlobalEnv></span></code></pre></div>
+<p>The structure of the underlying object is slightly different depending on whether you have a one-sided or two-sided formula:</p>
+<ul>
+<li><p>One-sided formulas have length two:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">length</span>(f)
+<span class="co">#> [1] 2</span>
+<span class="co"># The 1st element is always ~</span>
+f[[<span class="dv">1</span>]]
+<span class="co">#> `~`</span>
+<span class="co"># The 2nd element is the RHS</span>
+f[[<span class="dv">2</span>]]
+<span class="co">#> x + y + z</span></code></pre></div></li>
+<li><p>Two-sided formulas have length three:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">g <-<span class="st"> </span>y ~<span class="st"> </span>x +<span class="st"> </span>z
+<span class="kw">length</span>(g)
+<span class="co">#> [1] 3</span>
+<span class="co"># The 1st element is still ~</span>
+g[[<span class="dv">1</span>]]
+<span class="co">#> `~`</span>
+<span class="co"># But now the 2nd element is the LHS</span>
+g[[<span class="dv">2</span>]]
+<span class="co">#> y</span>
+<span class="co"># And the 3rd element is the RHS</span>
+g[[<span class="dv">3</span>]]
+<span class="co">#> x + z</span></code></pre></div></li>
+</ul>
+<p>To abstract away these differences, lazyeval provides <code>f_rhs()</code> and <code>f_lhs()</code> to access either side of the formula, and <code>f_env()</code> to access its environment:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">f_rhs</span>(f)
+<span class="co">#> x + y + z</span>
+<span class="kw">f_lhs</span>(f)
+<span class="co">#> NULL</span>
+<span class="kw">f_env</span>(f)
+<span class="co">#> <environment: R_GlobalEnv></span>
+
+<span class="kw">f_rhs</span>(g)
+<span class="co">#> x + z</span>
+<span class="kw">f_lhs</span>(g)
+<span class="co">#> y</span>
+<span class="kw">f_env</span>(g)
+<span class="co">#> <environment: R_GlobalEnv></span></code></pre></div>
+</div>
+<div id="evaluating-a-formula" class="section level3">
+<h3>Evaluating a formula</h3>
+<p>A formula captures delays the evaluation of an expression so you can later evaluate it with <code>f_eval()</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">f <-<span class="st"> </span><span class="er">~</span><span class="st"> </span><span class="dv">1</span> +<span class="st"> </span><span class="dv">2</span> +<span class="st"> </span><span class="dv">3</span>
+f
+<span class="co">#> ~1 + 2 + 3</span>
+<span class="kw">f_eval</span>(f)
+<span class="co">#> [1] 6</span></code></pre></div>
+<p>This allows you to use a formula as a robust way of delaying evaluation, cleanly separating the creation of the formula from its evaluation. Because formulas capture the code and context, you get the correct result even when a formula is created and evaluated in different places. In the following example, note that the value of <code>x</code> inside <code>add_1000()</code> is used:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">x <-<span class="st"> </span><span class="dv">1</span>
+add_1000 <-<span class="st"> </span>function(x) {
+  ~<span class="st"> </span><span class="dv">1000</span> +<span class="st"> </span>x
+}
+
+<span class="kw">add_1000</span>(<span class="dv">3</span>)
+<span class="co">#> ~1000 + x</span>
+<span class="co">#> <environment: 0x7fd4dbeca590></span>
+<span class="kw">f_eval</span>(<span class="kw">add_1000</span>(<span class="dv">3</span>))
+<span class="co">#> [1] 1003</span></code></pre></div>
+<p>It can be hard to see what’s going on when looking at a formula because important values are stored in the environment, which is largely opaque. You can use <code>f_unwrap()</code> to replace names with their corresponding values:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">f_unwrap</span>(<span class="kw">add_1000</span>(<span class="dv">3</span>))
+<span class="co">#> ~1000 + 3</span></code></pre></div>
+</div>
+<div id="non-standard-scoping" class="section level3">
+<h3>Non-standard scoping</h3>
+<p><code>f_eval()</code> has an optional second argument: a named list (or data frame) that overrides values found in the formula’s environment.</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">y <-<span class="st"> </span><span class="dv">100</span>
+<span class="kw">f_eval</span>(~<span class="st"> </span>y)
+<span class="co">#> [1] 100</span>
+<span class="kw">f_eval</span>(~<span class="st"> </span>y, <span class="dt">data =</span> <span class="kw">list</span>(<span class="dt">y =</span> <span class="dv">10</span>))
+<span class="co">#> [1] 10</span>
+
+<span class="co"># Can mix variables in environment and data argument</span>
+<span class="kw">f_eval</span>(~<span class="st"> </span>x +<span class="st"> </span>y, <span class="dt">data =</span> <span class="kw">list</span>(<span class="dt">x =</span> <span class="dv">10</span>))
+<span class="co">#> [1] 110</span>
+<span class="co"># Can even supply functions</span>
+<span class="kw">f_eval</span>(~<span class="st"> </span><span class="kw">f</span>(y), <span class="dt">data =</span> <span class="kw">list</span>(<span class="dt">f =</span> function(x) x *<span class="st"> </span><span class="dv">3</span>))
+<span class="co">#> [1] 300</span></code></pre></div>
+<p>This makes it very easy to implement non-standard scoping:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">f_eval</span>(~<span class="st"> </span><span class="kw">mean</span>(cyl), <span class="dt">data =</span> mtcars)
+<span class="co">#> [1] 6.1875</span></code></pre></div>
+<p>One challenge with non-standard scoping is that we’ve introduced some ambiguity. For example, in the code below does <code>x</code> come from <code>mydata</code> or the environment?</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">f_eval</span>(~<span class="st"> </span>x, <span class="dt">data =</span> mydata)</code></pre></div>
+<p>You can’t tell without knowing whether or not <code>mydata</code> has a variable called <code>x</code>. To overcome this problem, <code>f_eval()</code> provides two pronouns:</p>
+<ul>
+<li><code>.data</code> is bound to the data frame.</li>
+<li><code>.env</code> is bound to the formula environment.</li>
+</ul>
+<p>They both start with <code>.</code> to minimise the chances of clashing with existing variables.</p>
+<p>With these pronouns we can rewrite the previous formula to remove the ambiguity:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">mydata <-<span class="st"> </span><span class="kw">data.frame</span>(<span class="dt">x =</span> <span class="dv">100</span>, <span class="dt">y =</span> <span class="dv">1</span>)
+x <-<span class="st"> </span><span class="dv">10</span>
+
+<span class="kw">f_eval</span>(~<span class="st"> </span>.env$x, <span class="dt">data =</span> mydata)
+<span class="co">#> [1] 10</span>
+<span class="kw">f_eval</span>(~<span class="st"> </span>.data$x, <span class="dt">data =</span> mydata)
+<span class="co">#> [1] 100</span></code></pre></div>
+<p>If the variable or object doesn’t exist, you’ll get an informative error:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">f_eval</span>(~<span class="st"> </span>.env$z, <span class="dt">data =</span> mydata)
+<span class="co">#> Error: Object 'z' not found in environment</span>
+<span class="kw">f_eval</span>(~<span class="st"> </span>.data$z, <span class="dt">data =</span> mydata)
+<span class="co">#> Error: Variable 'z' not found in data</span></code></pre></div>
+</div>
+<div id="unquoting" class="section level3">
+<h3>Unquoting</h3>
+<p><code>f_eval()</code> has one more useful trick up its sleeve: unquoting. Unquoting allows you to write functions where the user supplies part of the formula. For example, the following function allows you to compute the mean of any column (or any function of a column):</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">df_mean <-<span class="st"> </span>function(df, variable) {
+  <span class="kw">f_eval</span>(~<span class="st"> </span><span class="kw">mean</span>(<span class="kw">uq</span>(variable)), <span class="dt">data =</span> df)
+}
+
+<span class="kw">df_mean</span>(mtcars, ~<span class="st"> </span>cyl)
+<span class="co">#> [1] 6.1875</span>
+<span class="kw">df_mean</span>(mtcars, ~<span class="st"> </span>disp *<span class="st"> </span><span class="fl">0.01638</span>)
+<span class="co">#> [1] 3.779224</span>
+<span class="kw">df_mean</span>(mtcars, ~<span class="st"> </span><span class="kw">sqrt</span>(mpg))
+<span class="co">#> [1] 4.43477</span></code></pre></div>
+<p>To see how this works, we can use <code>f_interp()</code> which <code>f_eval()</code> calls internally (you shouldn’t call it in your own code, but it’s useful for debugging). The key is <code>uq()</code>: <code>uq()</code> evaluates its first (and only) argument and inserts the value into the formula:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">variable <-<span class="st"> </span><span class="er">~</span>cyl
+<span class="kw">f_interp</span>(~<span class="st"> </span><span class="kw">mean</span>(<span class="kw">uq</span>(variable)))
+<span class="co">#> ~mean(cyl)</span>
+
+variable <-<span class="st"> </span><span class="er">~</span><span class="st"> </span>disp *<span class="st"> </span><span class="fl">0.01638</span>
+<span class="kw">f_interp</span>(~<span class="st"> </span><span class="kw">mean</span>(<span class="kw">uq</span>(variable)))
+<span class="co">#> ~mean(disp * 0.01638)</span></code></pre></div>
+<p>Unquoting allows you to create code “templates”, where you write most of the expression, while still allowing the user to control important components. You can even use <code>uq()</code> to change the function being called:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">f <-<span class="st"> </span><span class="er">~</span><span class="st"> </span>mean
+<span class="kw">f_interp</span>(~<span class="st"> </span><span class="kw">uq</span>(f)(<span class="kw">uq</span>(variable)))
+<span class="co">#> ~mean(disp * 0.01638)</span></code></pre></div>
+<p>Note that <code>uq()</code> only takes the RHS of a formula, which makes it difficult to insert literal formulas into a call:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">formula <-<span class="st"> </span>y ~<span class="st"> </span>x
+<span class="kw">f_interp</span>(~<span class="st"> </span><span class="kw">lm</span>(<span class="kw">uq</span>(formula), <span class="dt">data =</span> df))
+<span class="co">#> ~lm(x, data = df)</span></code></pre></div>
+<p>You can instead use <code>uqf()</code> which uses the whole formula, not just the RHS:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">f_interp</span>(~<span class="st"> </span><span class="kw">lm</span>(<span class="kw">uqf</span>(formula), <span class="dt">data =</span> df))
+<span class="co">#> ~lm(y ~ x, data = df)</span></code></pre></div>
+<p>Unquoting is powerful, but it only allows you to modify a single argument: it doesn’t allow you to add an arbitrary number of arguments. To do that, you’ll need “unquote-splice”, or <code>uqs()</code>. The first (and only) argument to <code>uqs()</code> should be a list of arguments to be spliced into the call:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">variable <-<span class="st"> </span><span class="er">~</span><span class="st"> </span>x
+extra_args <-<span class="st"> </span><span class="kw">list</span>(<span class="dt">na.rm =</span> <span class="ot">TRUE</span>, <span class="dt">trim =</span> <span class="fl">0.9</span>)
+<span class="kw">f_interp</span>(~<span class="st"> </span><span class="kw">mean</span>(<span class="kw">uq</span>(variable), <span class="kw">uqs</span>(extra_args)))
+<span class="co">#> ~mean(x, na.rm = TRUE, trim = 0.9)</span></code></pre></div>
+</div>
+<div id="exercises-1" class="section level3">
+<h3>Exercises</h3>
+<ol style="list-style-type: decimal">
+<li><p>Create a wrapper around <code>lm()</code> that allows the user to supply the response and predictors as two separate formulas.</p></li>
+<li><p>Compare and contrast <code>f_eval()</code> with <code>with()</code>.</p></li>
+<li><p>Why does this code work even though <code>f</code> is defined in two places? (And one of them is not a function).</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">f <-<span class="st"> </span>function(x) x +<span class="st"> </span><span class="dv">1</span>
+<span class="kw">f_eval</span>(~<span class="st"> </span><span class="kw">f</span>(<span class="dv">10</span>), <span class="kw">list</span>(<span class="dt">f =</span> <span class="st">"a"</span>))
+<span class="co">#> [1] 11</span></code></pre></div></li>
+</ol>
+</div>
+</div>
+<div id="non-standard-scoping-1" class="section level2">
+<h2>Non-standard scoping</h2>
+<p>Non-standard scoping (NSS) is an important part of R because it makes it easy to write functions tailored for interactive data exploration. These functions require less typing, at the cost of some ambiguity and “magic”. This is a good trade-off for interactive data exploration because you want to get ideas out of your head and into the computer as quickly as possible. If a function does make a bad guess, you’ll spot it quickly because you’re working interactively.</p>
+<p>There are three challenges to implementing non-standard scoping:</p>
+<ol style="list-style-type: decimal">
+<li><p>You must correctly delay the evaluation of a function argument, capturing both the computation (the expression), and the context (the environment). I recommend making this explicit by requiring the user to “quote” any NSS arguments with <code>~</code>, and then evaluating explicit with <code>f_eval()</code>.</p></li>
+<li><p>When writing functions that use NSS-functions, you need some way to avoid the automatic lookup and be explicit about where objects should be found. <code>f_eval()</code> solves this problem with the <code>.data.</code> and <code>.env</code> pronouns.</p></li>
+<li><p>You need some way to allow the user to supply parts of a formula. <code>f_eval()</code> solves this with unquoting.</p></li>
+</ol>
+<p>To illustrate these challenges, I will implement a <code>sieve()</code> function that works similarly to <code>base::subset()</code> or <code>dplyr::filter()</code>. The goal of <code>sieve()</code> is to make it easy to select observations that match criteria defined by a logical expression. <code>sieve()</code> has three advantages over <code>[</code>:</p>
+<ol style="list-style-type: decimal">
+<li><p>It is much more compact when the condition uses many variables, because you don’t need to repeat the name of the data frame many times.</p></li>
+<li><p>It drops rows where the condition evaluates to <code>NA</code>, rather than filling them with <code>NA</code>s.</p></li>
+<li><p>It always returns a data frame.</p></li>
+</ol>
+<p>The implementation of <code>sieve()</code> is straightforward. First we use <code>f_eval()</code> to perform NSS. Then we then check that we have a logical vector, replace <code>NA</code>s with <code>FALSE</code>, and subset with <code>[</code>.</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">sieve <-<span class="st"> </span>function(df, condition) {
+  rows <-<span class="st"> </span><span class="kw">f_eval</span>(condition, df)
+  if (!<span class="kw">is.logical</span>(rows)) {
+    <span class="kw">stop</span>(<span class="st">"`condition` must be logical."</span>, <span class="dt">call. =</span> <span class="ot">FALSE</span>)
+  }
+  
+  rows[<span class="kw">is.na</span>(rows)] <-<span class="st"> </span><span class="ot">FALSE</span>
+  df[rows, , drop =<span class="st"> </span><span class="ot">FALSE</span>]
+}
+
+df <-<span class="st"> </span><span class="kw">data.frame</span>(<span class="dt">x =</span> <span class="dv">1</span>:<span class="dv">5</span>, <span class="dt">y =</span> <span class="dv">5</span>:<span class="dv">1</span>)
+<span class="kw">sieve</span>(df, ~<span class="st"> </span>x <=<span class="st"> </span><span class="dv">2</span>)
+<span class="co">#>   x y</span>
+<span class="co">#> 1 1 5</span>
+<span class="co">#> 2 2 4</span>
+<span class="kw">sieve</span>(df, ~<span class="st"> </span>x ==<span class="st"> </span>y)
+<span class="co">#>   x y</span>
+<span class="co">#> 3 3 3</span></code></pre></div>
+<div id="programming-with-sieve" class="section level3">
+<h3>Programming with <code>sieve()</code></h3>
+<p>Imagine that you’ve written some code that looks like this:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">sieve</span>(march, ~<span class="st"> </span>x ><span class="st"> </span><span class="dv">100</span>)
+<span class="kw">sieve</span>(april, ~<span class="st"> </span>x ><span class="st"> </span><span class="dv">50</span>)
+<span class="kw">sieve</span>(june, ~<span class="st"> </span>x ><span class="st"> </span><span class="dv">45</span>)
+<span class="kw">sieve</span>(july, ~<span class="st"> </span>x ><span class="st"> </span><span class="dv">17</span>)</code></pre></div>
+<p>(This is a contrived example, but it illustrates all of the important issues you’ll need to consider when writing more useful functions.)</p>
+<p>Instead of continuing to copy-and-paste your code, you decide to wrap up the common behaviour in a function:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">threshold_x <-<span class="st"> </span>function(df, threshold) {
+  <span class="kw">sieve</span>(df, ~<span class="st"> </span>x ><span class="st"> </span>threshold)
+}
+<span class="kw">threshold_x</span>(df, <span class="dv">3</span>)
+<span class="co">#>   x y</span>
+<span class="co">#> 4 4 2</span>
+<span class="co">#> 5 5 1</span></code></pre></div>
+<p>There are two ways that this function might fail:</p>
+<ol style="list-style-type: decimal">
+<li><p>The data frame might not have a variable called <code>x</code>. This will fail unless there’s a variable called <code>x</code> hanging around in the global environment:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">rm</span>(x)
+df2 <-<span class="st"> </span><span class="kw">data.frame</span>(<span class="dt">y =</span> <span class="dv">5</span>:<span class="dv">1</span>)
+
+<span class="co"># Throws an error</span>
+<span class="kw">threshold_x</span>(df2, <span class="dv">3</span>)
+<span class="co">#> Error in eval(expr, envir, enclos): object 'x' not found</span>
+
+<span class="co"># Silently gives the incorrect result!</span>
+x <-<span class="st"> </span><span class="dv">5</span>
+<span class="kw">threshold_x</span>(df2, <span class="dv">3</span>)
+<span class="co">#>   y</span>
+<span class="co">#> 1 5</span>
+<span class="co">#> 2 4</span>
+<span class="co">#> 3 3</span>
+<span class="co">#> 4 2</span>
+<span class="co">#> 5 1</span></code></pre></div></li>
+<li><p>The data frame might have a variable called <code>threshold</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">df3 <-<span class="st"> </span><span class="kw">data.frame</span>(<span class="dt">x =</span> <span class="dv">1</span>:<span class="dv">5</span>, <span class="dt">y =</span> <span class="dv">5</span>:<span class="dv">1</span>, <span class="dt">threshold =</span> <span class="dv">4</span>)
+<span class="kw">threshold_x</span>(df3, <span class="dv">3</span>)
+<span class="co">#>   x y threshold</span>
+<span class="co">#> 5 5 1         4</span></code></pre></div></li>
+</ol>
+<p>These failures are partiuclarly pernicious because instead of throwing an error they silently produce the wrong answer. Both failures arise because <code>f_eval()</code> introduces ambiguity by looking in two places for each name: the supplied data and formula environment.</p>
+<p>To make <code>threshold_x()</code> more reliable, we need to be more explicit by using the <code>.data</code> and <code>.env</code> pronouns:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">threshold_x <-<span class="st"> </span>function(df, threshold) {
+  <span class="kw">sieve</span>(df, ~<span class="st"> </span>.data$x ><span class="st"> </span>.env$threshold)
+}
+
+<span class="kw">threshold_x</span>(df2, <span class="dv">3</span>)
+<span class="co">#> Error: Variable 'x' not found in data</span>
+<span class="kw">threshold_x</span>(df3, <span class="dv">3</span>)
+<span class="co">#>   x y threshold</span>
+<span class="co">#> 4 4 2         4</span>
+<span class="co">#> 5 5 1         4</span></code></pre></div>
+<p>Here <code>.env</code> is bound to the environment where <code>~</code> is evaluated, namely the inside of <code>threshold_x()</code>.</p>
+</div>
+<div id="adding-arguments" class="section level3">
+<h3>Adding arguments</h3>
+<p>The <code>threshold_x()</code> function is not very useful because it’s bound to a specific variable. It would be more powerful if we could vary both the threshold and the variable it applies to. We can do that by taking an additional argument to specify which variable to use.</p>
+<p>One simple approach is to use a string and <code>[[</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">threshold <-<span class="st"> </span>function(df, variable, threshold) {
+  <span class="kw">stopifnot</span>(<span class="kw">is.character</span>(variable), <span class="kw">length</span>(variable) ==<span class="st"> </span><span class="dv">1</span>)
+  
+  <span class="kw">sieve</span>(df, ~<span class="st"> </span>.data[[.env$variable]] ><span class="st"> </span>.env$threshold)
+}
+<span class="kw">threshold</span>(df, <span class="st">"x"</span>, <span class="dv">4</span>)
+<span class="co">#>   x y</span>
+<span class="co">#> 5 5 1</span></code></pre></div>
+<p>This is a simple and robust solution, but only allows us to use an existing variable, not an arbitrary expression like <code>sqrt(x)</code>.</p>
+<p>A more general solution is to allow the user to supply a formula, and use unquoting:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">threshold <-<span class="st"> </span>function(df, <span class="dt">variable =</span> ~x, <span class="dt">threshold =</span> <span class="dv">0</span>) {
+  <span class="kw">sieve</span>(df, ~<span class="st"> </span><span class="kw">uq</span>(variable) ><span class="st"> </span>.env$threshold)
+}
+
+<span class="kw">threshold</span>(df, ~<span class="st"> </span>x, <span class="dv">4</span>)
+<span class="co">#>   x y</span>
+<span class="co">#> 5 5 1</span>
+<span class="kw">threshold</span>(df, ~<span class="st"> </span><span class="kw">abs</span>(x -<span class="st"> </span>y), <span class="dv">2</span>)
+<span class="co">#>   x y</span>
+<span class="co">#> 1 1 5</span>
+<span class="co">#> 5 5 1</span></code></pre></div>
+<p>In this case, it’s the responsibility of the user to ensure the <code>variable</code> is specified unambiguously. <code>f_eval()</code> is designed so that <code>.data</code> and <code>.env</code> work even when evaluated inside of <code>uq()</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">x <-<span class="st"> </span><span class="dv">3</span>
+<span class="kw">threshold</span>(df, ~<span class="st"> </span>.data$x -<span class="st"> </span>.env$x, <span class="dv">0</span>)
+<span class="co">#>   x y</span>
+<span class="co">#> 4 4 2</span>
+<span class="co">#> 5 5 1</span></code></pre></div>
+</div>
+<div id="dot-dot-dot" class="section level3">
+<h3>Dot-dot-dot</h3>
+<p>There is one more tool that you might find useful for functions that take <code>...</code>. For example, the code below implements a function similar to <code>dplyr::mutate()</code> or <code>base::transform()</code>.</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">mogrify <-<span class="st"> </span>function(<span class="st">`</span><span class="dt">_df</span><span class="st">`</span>, ...) {
+  args <-<span class="st"> </span><span class="kw">list</span>(...)
+  
+  for (nm in <span class="kw">names</span>(args)) {
+    <span class="st">`</span><span class="dt">_df</span><span class="st">`</span>[[nm]] <-<span class="st"> </span><span class="kw">f_eval</span>(args[[nm]], <span class="st">`</span><span class="dt">_df</span><span class="st">`</span>)
+  }
+  
+  <span class="st">`</span><span class="dt">_df</span><span class="st">`</span>
+}</code></pre></div>
+<p>(NB: the first argument is a non-syntactic name (i.e. it requires quoting with <code>`</code>) so it doesn’t accidentally match one of the names of the new variables.)</p>
+<p><code>transmogrifty()</code> makes it easy to add new variables to a data frame:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">df <-<span class="st"> </span><span class="kw">data.frame</span>(<span class="dt">x =</span> <span class="dv">1</span>:<span class="dv">5</span>, <span class="dt">y =</span> <span class="kw">sample</span>(<span class="dv">5</span>))
+<span class="kw">mogrify</span>(df, <span class="dt">z =</span> ~<span class="st"> </span>x +<span class="st"> </span>y, <span class="dt">z2 =</span> ~<span class="st"> </span>z *<span class="st"> </span><span class="dv">2</span>)
+<span class="co">#>   x y z z2</span>
+<span class="co">#> 1 1 3 4  8</span>
+<span class="co">#> 2 2 4 6 12</span>
+<span class="co">#> 3 3 2 5 10</span>
+<span class="co">#> 4 4 5 9 18</span>
+<span class="co">#> 5 5 1 6 12</span></code></pre></div>
+<p>One problem with this implementation is that it’s hard to specify the names of the generated variables. Imagine you want a function where the name and expression are in separate variables. This is awkward because the variable name is supplied as an argument name to <code>mogrify()</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">add_variable <-<span class="st"> </span>function(df, name, expr) {
+  <span class="kw">do.call</span>(<span class="st">"mogrify"</span>, <span class="kw">c</span>(<span class="kw">list</span>(df), <span class="kw">setNames</span>(<span class="kw">list</span>(expr), name)))
+}
+<span class="kw">add_variable</span>(df, <span class="st">"z"</span>, ~<span class="st"> </span>x +<span class="st"> </span>y)
+<span class="co">#>   x y z</span>
+<span class="co">#> 1 1 3 4</span>
+<span class="co">#> 2 2 4 6</span>
+<span class="co">#> 3 3 2 5</span>
+<span class="co">#> 4 4 5 9</span>
+<span class="co">#> 5 5 1 6</span></code></pre></div>
+<p>Lazyeval provides the <code>f_list()</code> function to make writing this sort of function a little easier. It takes a list of formulas and evaluates the LHS of each formula (if present) to rename the elements:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">f_list</span>(<span class="st">"x"</span> ~<span class="st"> </span>y, <span class="dt">z =</span> ~z)
+<span class="co">#> $x</span>
+<span class="co">#> ~y</span>
+<span class="co">#> </span>
+<span class="co">#> $z</span>
+<span class="co">#> ~z</span></code></pre></div>
+<p>If we tweak <code>mogrify()</code> to use <code>f_list()</code> instead of <code>list()</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">mogrify <-<span class="st"> </span>function(<span class="st">`</span><span class="dt">_df</span><span class="st">`</span>, ...) {
+  args <-<span class="st"> </span><span class="kw">f_list</span>(...)
+  
+  for (nm in <span class="kw">names</span>(args)) {
+    <span class="st">`</span><span class="dt">_df</span><span class="st">`</span>[[nm]] <-<span class="st"> </span><span class="kw">f_eval</span>(args[[nm]], <span class="st">`</span><span class="dt">_df</span><span class="st">`</span>)
+  }
+  
+  <span class="st">`</span><span class="dt">_df</span><span class="st">`</span>
+}</code></pre></div>
+<p><code>add_new()</code> becomes much simpler:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">add_variable <-<span class="st"> </span>function(df, name, expr) {
+  <span class="kw">mogrify</span>(df, name ~<span class="st"> </span><span class="kw">uq</span>(expr))
+}
+<span class="kw">add_variable</span>(df, <span class="st">"z"</span>, ~<span class="st"> </span>x +<span class="st"> </span>y)
+<span class="co">#>   x y z</span>
+<span class="co">#> 1 1 3 4</span>
+<span class="co">#> 2 2 4 6</span>
+<span class="co">#> 3 3 2 5</span>
+<span class="co">#> 4 4 5 9</span>
+<span class="co">#> 5 5 1 6</span></code></pre></div>
+</div>
+<div id="exercises-2" class="section level3">
+<h3>Exercises</h3>
+<ol style="list-style-type: decimal">
+<li><p>Write a function that selects all rows of <code>df</code> where <code>variable</code> is greater than its mean. Make the function more general by allowing the user to specify a function to use instead of <code>mean()</code> (e.g. <code>median()</code>).</p></li>
+<li><p>Create a version of <code>mogrify()</code> where the first argument is <code>x</code>? What happens if you try to create a new variable called <code>x</code>?</p></li>
+</ol>
+</div>
+</div>
+<div id="non-standard-evaluation" class="section level2">
+<h2>Non-standard evaluation</h2>
+<p>In some situations you might want to eliminate the formula altogether, and allow the user to type expressions directly. I was once much enamoured with this approach (witness ggplot2, dplyr, …). However, I now think that it should be used sparingly because explict quoting with <code>~</code> leads to simpler code, and makes it more clear to the user that something special is going on.</p>
+<p>That said, lazyeval does allow you to eliminate the <code>~</code> if you really want to. In this case, I recommend having both a NSE and SE version of the function. The SE version, which takes formuals, should have suffix <code>_</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">sieve_ <-<span class="st"> </span>function(df, condition) {
+  rows <-<span class="st"> </span><span class="kw">f_eval</span>(condition, df)
+  if (!<span class="kw">is.logical</span>(rows)) {
+    <span class="kw">stop</span>(<span class="st">"`condition` must be logical."</span>, <span class="dt">call. =</span> <span class="ot">FALSE</span>)
+  }
+  
+  rows[<span class="kw">is.na</span>(rows)] <-<span class="st"> </span><span class="ot">FALSE</span>
+  df[rows, , drop =<span class="st"> </span><span class="ot">FALSE</span>]
+}</code></pre></div>
+<p>Then create the NSE version which doesn’t need the explicit formula. The key is the use of <code>f_capture()</code> which takes an unevaluated argument (a promise) and captures it as a formula:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">sieve <-<span class="st"> </span>function(df, expr) {
+  <span class="kw">sieve_</span>(df, <span class="kw">f_capture</span>(expr))
+}
+<span class="kw">sieve</span>(df, x ==<span class="st"> </span><span class="dv">1</span>)
+<span class="co">#>   x y</span>
+<span class="co">#> 1 1 3</span></code></pre></div>
+<p>If you’re familiar with <code>substitute()</code> you might expect the same drawbacks to apply. However, <code>f_capture()</code> is smart enough to follow a chain of promises back to the original value, so, for example, this code works fine:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">scramble <-<span class="st"> </span>function(df) {
+  df[<span class="kw">sample</span>(<span class="kw">nrow</span>(df)), , drop =<span class="st"> </span><span class="ot">FALSE</span>]
+}
+subscramble <-<span class="st"> </span>function(df, expr) {
+  <span class="kw">scramble</span>(<span class="kw">sieve</span>(df, expr))
+}
+<span class="kw">subscramble</span>(df, x <<span class="st"> </span><span class="dv">4</span>)
+<span class="co">#>   x y</span>
+<span class="co">#> 2 2 4</span>
+<span class="co">#> 3 3 2</span>
+<span class="co">#> 1 1 3</span></code></pre></div>
+<div id="dot-dot-dot-1" class="section level3">
+<h3>Dot-dot-dot</h3>
+<p>If you want a <code>...</code> function that doesn’t require formulas, I recommend that the SE version take a list of arguments, and the NSE version uses <code>dots_capture()</code> to capture multiple arguments as a list of formulas.</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">mogrify_ <-<span class="st"> </span>function(<span class="st">`</span><span class="dt">_df</span><span class="st">`</span>, args) {
+  args <-<span class="st"> </span><span class="kw">as_f_list</span>(args)
+  
+  for (nm in <span class="kw">names</span>(args)) {
+    <span class="st">`</span><span class="dt">_df</span><span class="st">`</span>[[nm]] <-<span class="st"> </span><span class="kw">f_eval</span>(args[[nm]], <span class="st">`</span><span class="dt">_df</span><span class="st">`</span>)
+  }
+  
+  <span class="st">`</span><span class="dt">_df</span><span class="st">`</span>
+}
+
+mogrify <-<span class="st"> </span>function(<span class="st">`</span><span class="dt">_df</span><span class="st">`</span>, ...) {
+  <span class="kw">mogrify_</span>(<span class="st">`</span><span class="dt">_df</span><span class="st">`</span>, <span class="kw">dots_capture</span>(...))
+}</code></pre></div>
+</div>
+<div id="exercises-3" class="section level3">
+<h3>Exercises</h3>
+<ol style="list-style-type: decimal">
+<li>Recreate <code>subscramble()</code> using <code>base::subset()</code> instead of <code>sieve()</code>. Why does it fail?</li>
+</ol>
+</div>
+</div>
+<div id="metaprogramming" class="section level2">
+<h2>Metaprogramming</h2>
+<p>The final use of non-standard evaluation is to do metaprogramming. This is a catch-all term that encompasses any function that does computation on an unevaluated expression. You can learn about metaprogrgramming in <a href="http://adv-r.had.co.nz/Expressions.html" class="uri">http://adv-r.had.co.nz/Expressions.html</a>, particularly <a href="http://adv-r.had.co.nz/Expressions.html#ast-funs" class="uri">http://adv-r.had.co.nz/Expressions.html#ast-funs</a>. Over time, the goal is to mov [...]
+</div>
+<div class="footnotes">
+<hr />
+<ol>
+<li id="fn1"><p>Currently neither ggplot2 nor dplyr actually use these tools since I’ve only just figured it out. But I’ll be working hard to make sure all my packages are consistent in the near future.<a href="#fnref1">↩</a></p></li>
+</ol>
+</div>
+
+
+
+<!-- dynamically load mathjax for compatibility with self-contained -->
+<script>
+  (function () {
+    var script = document.createElement("script");
+    script.type = "text/javascript";
+    script.src  = "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
+    document.getElementsByTagName("head")[0].appendChild(script);
+  })();
+</script>
+
+</body>
+</html>
diff --git a/man/all_dots.Rd b/man/all_dots.Rd
new file mode 100644
index 0000000..77beb12
--- /dev/null
+++ b/man/all_dots.Rd
@@ -0,0 +1,24 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/lazy-as.R
+\name{all_dots}
+\alias{all_dots}
+\title{Combine explicit and implicit dots.}
+\usage{
+all_dots(.dots, ..., all_named = FALSE)
+}
+\arguments{
+\item{.dots}{A list of lazy objects}
+
+\item{...}{Individual lazy objects}
+
+\item{all_named}{If \code{TRUE}, uses \code{\link{auto_name}} to ensure
+every component has a name.}
+}
+\value{
+A \code{\link{lazy_dots}}
+}
+\description{
+Combine explicit and implicit dots.
+}
+\keyword{internal}
+
diff --git a/man/as.lazy.Rd b/man/as.lazy.Rd
new file mode 100644
index 0000000..2ae4d87
--- /dev/null
+++ b/man/as.lazy.Rd
@@ -0,0 +1,36 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/lazy-as.R
+\name{as.lazy}
+\alias{as.lazy}
+\alias{as.lazy_dots}
+\title{Convert an object to a lazy expression or lazy dots.}
+\usage{
+as.lazy(x, env = baseenv())
+
+as.lazy_dots(x, env)
+}
+\arguments{
+\item{x}{An R object. Current methods for \code{as.lazy()} convert formulas,
+character vectors, calls and names. Methods for \code{as.lazy_dots()}
+convert lists and character vectors (by calling \code{\link{lapply}()}
+with \code{as.lazy()}.)}
+
+\item{env}{Environment to use for objects that don't already have
+associated environment.}
+}
+\description{
+Convert an object to a lazy expression or lazy dots.
+}
+\examples{
+as.lazy(~ x + 1)
+as.lazy(quote(x + 1), globalenv())
+as.lazy("x + 1", globalenv())
+
+as.lazy_dots(list(~x, y = ~z + 1))
+as.lazy_dots(c("a", "b", "c"), globalenv())
+as.lazy_dots(~x)
+as.lazy_dots(quote(x), globalenv())
+as.lazy_dots(quote(f()), globalenv())
+as.lazy_dots(lazy(x))
+}
+
diff --git a/man/as_name.Rd b/man/as_name.Rd
new file mode 100644
index 0000000..cb95304
--- /dev/null
+++ b/man/as_name.Rd
@@ -0,0 +1,28 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/language.R
+\name{as_name}
+\alias{as_call}
+\alias{as_name}
+\title{Coerce an object to a name or call.}
+\usage{
+as_name(x)
+
+as_call(x)
+}
+\arguments{
+\item{x}{An object to coerce}
+}
+\description{
+These are a S3 generics with built-in methods for names, calls, formuals,
+and strings. The distinction between a name and a call is particularly
+important when coercing from a string. Coercing to a call will parse the
+string, coercing to a name will create a (potentially) non-syntactic name.
+}
+\examples{
+as_name("x + y")
+as_call("x + y")
+
+as_call(~ f)
+as_name(~ f())
+}
+
diff --git a/man/ast_.Rd b/man/ast_.Rd
new file mode 100644
index 0000000..dc75f85
--- /dev/null
+++ b/man/ast_.Rd
@@ -0,0 +1,32 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ast.R
+\name{ast_}
+\alias{ast}
+\alias{ast_}
+\title{Display a call (or expression) as a tree.}
+\usage{
+ast_(x, width = getOption("width"))
+
+ast(x)
+}
+\arguments{
+\item{x}{Quoted call, list of calls, or expression to display.}
+
+\item{width}{Display width, defaults to current width as reported by
+\code{getOption("width")}.}
+}
+\description{
+\code{ast_} takes a quoted expression; \code{ast} does the quoting
+for you.
+}
+\examples{
+ast(f(x, 1, g(), h(i())))
+ast(if (TRUE) 3 else 4)
+ast(function(a = 1, b = 2) {a + b + 10})
+ast(f(x)(y)(z))
+
+ast_(quote(f(x, 1, g(), h(i()))))
+ast_(quote(if (TRUE) 3 else 4))
+ast_(expression(1, 2, 3))
+}
+
diff --git a/man/auto_name.Rd b/man/auto_name.Rd
new file mode 100644
index 0000000..5c0aae0
--- /dev/null
+++ b/man/auto_name.Rd
@@ -0,0 +1,25 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/lazy-names.R
+\name{auto_name}
+\alias{auto_name}
+\title{Automatically name all components of a lazy dots.}
+\usage{
+auto_name(x, max_width = 40)
+}
+\arguments{
+\item{x}{A \code{\link{lazy_dots}}}
+
+\item{max_width}{Maximum number of characters to use}
+}
+\description{
+Any components missing a name will automatically get a name added by
+looking at the first \code{max_width} characters of the deparsed expression.
+}
+\examples{
+x <- lazy_dots(1 + 2, mean(mpg))
+auto_name(x)
+
+auto_name(list(~f, quote(x)))
+}
+\keyword{internal}
+
diff --git a/man/call_modify.Rd b/man/call_modify.Rd
new file mode 100644
index 0000000..010b112
--- /dev/null
+++ b/man/call_modify.Rd
@@ -0,0 +1,41 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/call.R
+\name{call_modify}
+\alias{call_modify}
+\alias{call_standardise}
+\title{Modify the arguments of a call.}
+\usage{
+call_modify(call, new_args, env = parent.frame())
+
+call_standardise(call, env = parent.frame())
+}
+\arguments{
+\item{call}{A call to modify. It is first standardised with
+\code{\link{call_standardise}}.}
+
+\item{new_args}{A named list of expressions (constants, names or calls)
+used to modify the call. Use \code{NULL} to remove arguments.}
+
+\item{env}{Environment in which to look up call value.}
+}
+\description{
+Modify the arguments of a call.
+}
+\examples{
+call <- quote(mean(x, na.rm = TRUE))
+call_standardise(call)
+
+# Modify an existing argument
+call_modify(call, list(na.rm = FALSE))
+call_modify(call, list(x = quote(y)))
+
+# Remove an argument
+call_modify(call, list(na.rm = NULL))
+
+# Add a new argument
+call_modify(call, list(trim = 0.1))
+
+# Add an explicit missing argument
+call_modify(call, list(na.rm = quote(expr = )))
+}
+
diff --git a/man/call_new.Rd b/man/call_new.Rd
new file mode 100644
index 0000000..b13ac49
--- /dev/null
+++ b/man/call_new.Rd
@@ -0,0 +1,28 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/call.R
+\name{call_new}
+\alias{call_new}
+\title{Create a call by "hand"}
+\usage{
+call_new(f, ..., .args = list())
+}
+\arguments{
+\item{f}{Function to call. For \code{make_call}, either a string, a symbol
+or a quoted call. For \code{do_call}, a bare function name or call.}
+
+\item{..., .args}{Arguments to the call either in or out of a list}
+}
+\description{
+Create a call by "hand"
+}
+\examples{
+# f can either be a string, a symbol or a call
+call_new("f", a = 1)
+call_new(quote(f), a = 1)
+call_new(quote(f()), a = 1)
+
+#' Can supply arguments individually or in a list
+call_new(quote(f), a = 1, b = 2)
+call_new(quote(f), .args = list(a = 1, b = 2))
+}
+
diff --git a/man/common_env.Rd b/man/common_env.Rd
new file mode 100644
index 0000000..e098cf4
--- /dev/null
+++ b/man/common_env.Rd
@@ -0,0 +1,23 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/lazy-call.R
+\name{common_env}
+\alias{common_env}
+\title{Find common environment in list of lazy objects.}
+\usage{
+common_env(dots)
+}
+\arguments{
+\item{dots}{A list of lazy objects}
+}
+\description{
+If no common environment is found, will return \code{baseenv()}.
+}
+\examples{
+common_env(lazy_dots(a, b, c))
+
+f <- function(x) ~x
+common_env(list(f(1)))
+common_env(list(f(1), f(2)))
+}
+\keyword{internal}
+
diff --git a/man/expr_label.Rd b/man/expr_label.Rd
new file mode 100644
index 0000000..21a6c28
--- /dev/null
+++ b/man/expr_label.Rd
@@ -0,0 +1,59 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/expr.R
+\name{expr_label}
+\alias{expr_env}
+\alias{expr_find}
+\alias{expr_label}
+\alias{expr_text}
+\title{Find the expression associated with an argument}
+\usage{
+expr_label(x)
+
+expr_text(x, width = 60L, nlines = Inf)
+
+expr_find(x)
+
+expr_env(x, default_env)
+}
+\arguments{
+\item{x}{A promise (function argument)}
+
+\item{width}{Width of each line}
+
+\item{nlines}{Maximum number of lines to extract.}
+
+\item{default_env}{If supplied, \code{expr_env} will return this if the
+promise has already been forced. Otherwise it will throw an error.}
+}
+\description{
+\code{expr_find()} finds the full expression; \code{expr_text()} turns the
+expression into a single string; \code{expr_label()} formats it nicely for
+use in messages. \code{expr_env()} finds the environment associated with
+the expression.
+}
+\details{
+These functions never force promises, and will work even if a promise has
+previously been forced.
+}
+\examples{
+# Unlike substitute(), expr_find() finds the original expression
+f <- function(x) g(x)
+g <- function(y) h(y)
+h <- function(z) list(substitute(z), expr_find(z))
+
+f(1 + 2 + 3)
+
+expr_label(10)
+# Names a quoted with ``
+expr_label(x)
+# Strings are encoded
+expr_label("a\\nb")
+# Expressions are captured
+expr_label(a + b + c)
+# Long expressions are collapsed
+expr_label(foo({
+  1 + 2
+  print(x)
+}))
+}
+
diff --git a/man/f_capture.Rd b/man/f_capture.Rd
new file mode 100644
index 0000000..1f63a46
--- /dev/null
+++ b/man/f_capture.Rd
@@ -0,0 +1,40 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/f-capture.R
+\name{f_capture}
+\alias{dots_capture}
+\alias{f_capture}
+\title{Make a promise explicit by converting into a formula.}
+\usage{
+f_capture(x)
+
+dots_capture(..., .ignore_empty = TRUE)
+}
+\arguments{
+\item{x, ...}{An unevaluated promises}
+
+\item{.ignore_empty}{If \code{TRUE}, empty arguments will be silently
+dropped.}
+}
+\value{
+\code{f_capture} returns a formula; \code{dots_capture}
+  returns a list of formulas.
+}
+\description{
+This should be used sparingly if you want to implement true non-standard
+evaluation with 100\% magic. I recommend avoiding this unless you have
+strong reasons otherwise since requiring arguments to be formulas only
+adds one extra character to the inputs, and otherwise makes life much much
+simpler.
+}
+\examples{
+f_capture(a + b)
+dots_capture(a + b, c + d, e + f)
+
+# These functions will follow a chain of promises back to the
+# original definition
+f <- function(x) g(x)
+g <- function(y) h(y)
+h <- function(z) f_capture(z)
+f(a + b + c)
+}
+
diff --git a/man/f_eval.Rd b/man/f_eval.Rd
new file mode 100644
index 0000000..b7ad356
--- /dev/null
+++ b/man/f_eval.Rd
@@ -0,0 +1,75 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/f-eval.R
+\name{f_eval_rhs}
+\alias{f_eval}
+\alias{f_eval_lhs}
+\alias{f_eval_rhs}
+\alias{find_data}
+\title{Evaluate a formula}
+\usage{
+f_eval_rhs(f, data = NULL)
+
+f_eval_lhs(f, data = NULL)
+
+f_eval(f, data = NULL)
+
+find_data(x)
+}
+\arguments{
+\item{f}{A formula. Any expressions wrapped in \code{ uq() } will
+will be "unquoted", i.e. they will be evaluated, and the results inserted
+back into the formula. See \code{\link{f_interp}} for more details.}
+
+\item{data}{A list (or data frame). \code{find_data} is a generic used to
+find the data associated with a given object. If you want to make
+\code{f_eval} work for your own objects, you can define a method for this
+generic.}
+
+\item{x}{An object for which you want to find associated data.}
+}
+\description{
+\code{f_eval_rhs} evaluates the RHS of a formula and \code{f_eval_lhs}
+evaluates the LHS. \code{f_eval} is a shortcut for \code{f_eval_rhs} since
+that is what you most commonly need.
+}
+\details{
+If \code{data} is specified, variables will be looked for first in this
+object, and if not found in the environment of the formula.
+}
+\section{Pronouns}{
+
+When used with \code{data}, \code{f_eval} provides two pronouns to make it
+possible to be explicit about where you want values to come from:
+\code{.env} and \code{.data}. These are thin wrappers around \code{.data}
+and \code{.env} that throw errors if you try to access non-existent values.
+}
+\examples{
+f_eval(~ 1 + 2 + 3)
+
+# formulas automatically capture their enclosing environment
+foo <- function(x) {
+  y <- 10
+  ~ x + y
+}
+f <- foo(1)
+f
+f_eval(f)
+
+# If you supply data, f_eval will look their first:
+f_eval(~ cyl, mtcars)
+
+# To avoid ambiguity, you can use .env and .data pronouns to be
+# explicit:
+cyl <- 10
+f_eval(~ .data$cyl, mtcars)
+f_eval(~ .env$cyl, mtcars)
+
+# Imagine you are computing the mean of a variable:
+f_eval(~ mean(cyl), mtcars)
+# How can you change the variable that's being computed?
+# The easiest way is "unquote" with uq()
+# See ?f_interp for more details
+var <- ~ cyl
+f_eval(~ mean( uq(var) ), mtcars)
+}
+
diff --git a/man/f_interp.Rd b/man/f_interp.Rd
new file mode 100644
index 0000000..155452d
--- /dev/null
+++ b/man/f_interp.Rd
@@ -0,0 +1,60 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/f-interp.R
+\name{f_interp}
+\alias{f_interp}
+\alias{uq}
+\alias{uqf}
+\alias{uqs}
+\title{Interpolate a formula}
+\usage{
+f_interp(f, data = NULL)
+
+uq(x, data = NULL)
+
+uqf(x)
+
+uqs(x)
+}
+\arguments{
+\item{f}{A one-sided formula.}
+
+\item{data}{When called from inside \code{f_eval}, this is used to pass on
+the data so that nested formulas are evaluated in the correct environment.}
+
+\item{x}{For \code{uq} and \code{uqf}, a formula. For \code{uqs}, a
+a vector.}
+}
+\description{
+Interpolation replaces sub-expressions of the form \code{uq(x)} with
+the evaluated value of \code{x}, and inlines sub-expressions of
+the form \code{uqs(x)}.
+}
+\section{Theory}{
+
+Formally, \code{f_interp} is a quasiquote function, \code{uq()} is the
+unquote operator, and \code{uqs()} is the unquote splice operator.
+These terms have a rich history in LISP, and live on in modern languages
+like \href{Julia}{http://docs.julialang.org/en/release-0.1/manual/metaprogramming/}
+and \href{Racket}{https://docs.racket-lang.org/reference/quasiquote.html}.
+}
+\examples{
+f_interp(x ~ 1 + uq(1 + 2 + 3) + 10)
+
+# Use uqs() if you want to add multiple arguments to a function
+# It must evaluate to a list
+args <- list(1:10, na.rm = TRUE)
+f_interp(~ mean( uqs(args) ))
+
+# You can combine the two
+var <- quote(xyz)
+extra_args <- list(trim = 0.9)
+f_interp(~ mean( uq(var) , uqs(extra_args) ))
+
+foo <- function(n) {
+  ~ 1 + uq(n)
+}
+f <- foo(10)
+f
+f_interp(f)
+}
+
diff --git a/man/f_list.Rd b/man/f_list.Rd
new file mode 100644
index 0000000..b900ced
--- /dev/null
+++ b/man/f_list.Rd
@@ -0,0 +1,29 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/formula.R
+\name{f_list}
+\alias{as_f_list}
+\alias{f_list}
+\title{Build a named list from the LHS of formulas}
+\usage{
+f_list(...)
+
+as_f_list(x)
+}
+\arguments{
+\item{...}{Named arguments.}
+
+\item{x}{An existing list}
+}
+\value{
+A named list.
+}
+\description{
+\code{f_list} makes a new list; \code{as_f_list} takes an existing list.
+Both take the LHS of any two-sided formulas and evaluate it, replacing the
+current name with the result.
+}
+\examples{
+f_list("y" ~ x)
+f_list(a = "y" ~ a, ~ b, c = ~c)
+}
+
diff --git a/man/f_new.Rd b/man/f_new.Rd
new file mode 100644
index 0000000..91140e2
--- /dev/null
+++ b/man/f_new.Rd
@@ -0,0 +1,24 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/formula.R
+\name{f_new}
+\alias{f_new}
+\title{Create a formula object by "hand".}
+\usage{
+f_new(rhs, lhs = NULL, env = parent.frame())
+}
+\arguments{
+\item{lhs, rhs}{A call, name, or atomic vector.}
+
+\item{env}{An environment}
+}
+\value{
+A formula object
+}
+\description{
+Create a formula object by "hand".
+}
+\examples{
+f_new(quote(a))
+f_new(quote(a), quote(b))
+}
+
diff --git a/man/f_rhs.Rd b/man/f_rhs.Rd
new file mode 100644
index 0000000..850e13a
--- /dev/null
+++ b/man/f_rhs.Rd
@@ -0,0 +1,50 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/formula.R
+\name{f_rhs}
+\alias{f_env}
+\alias{f_env<-}
+\alias{f_lhs}
+\alias{f_lhs<-}
+\alias{f_rhs}
+\alias{f_rhs<-}
+\title{Get/set formula components.}
+\usage{
+f_rhs(f)
+
+f_rhs(x) <- value
+
+f_lhs(f)
+
+f_lhs(x) <- value
+
+f_env(f)
+
+f_env(x) <- value
+}
+\arguments{
+\item{f, x}{A formula}
+
+\item{value}{The value to replace with.}
+}
+\value{
+\code{f_rhs} and \code{f_lhs} return language objects (i.e.
+  atomic vectors of length 1, a name, or a call). \code{f_env}
+  returns an environment.
+}
+\description{
+\code{f_rhs} extracts the righthand side, \code{f_lhs} extracts the
+lefthand side, and \code{f_env} extracts the environment. All functions
+throw an error if \code{f} is not a formula.
+}
+\examples{
+f_rhs(~ 1 + 2 + 3)
+f_rhs(~ x)
+f_rhs(~ "A")
+f_rhs(1 ~ 2)
+
+f_lhs(~ y)
+f_lhs(x ~ y)
+
+f_env(~ x)
+}
+
diff --git a/man/f_text.Rd b/man/f_text.Rd
new file mode 100644
index 0000000..de38c1d
--- /dev/null
+++ b/man/f_text.Rd
@@ -0,0 +1,38 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/formula.R
+\name{f_text}
+\alias{f_label}
+\alias{f_text}
+\title{Turn RHS of formula into a string/label.}
+\usage{
+f_text(x, width = 60L, nlines = Inf)
+
+f_label(x)
+}
+\arguments{
+\item{x}{A formula.}
+
+\item{width}{Width of each line}
+
+\item{nlines}{Maximum number of lines to extract.}
+}
+\description{
+Equivalent of \code{\link{expr_text}()} and \code{\link{expr_label}()} for
+formulas.
+}
+\examples{
+f <- ~ a + b + bc
+f_text(f)
+f_label(f)
+
+# Names a quoted with ``
+f_label(~ x)
+# Strings are encoded
+f_label(~ "a\\nb")
+# Long expressions are collapsed
+f_label(~ foo({
+  1 + 2
+  print(x)
+}))
+}
+
diff --git a/man/f_unwrap.Rd b/man/f_unwrap.Rd
new file mode 100644
index 0000000..1bab013
--- /dev/null
+++ b/man/f_unwrap.Rd
@@ -0,0 +1,21 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/formula.R
+\name{f_unwrap}
+\alias{f_unwrap}
+\title{Unwrap a formula}
+\usage{
+f_unwrap(f)
+}
+\arguments{
+\item{f}{A formula to unwrap.}
+}
+\description{
+This interpolates values in the formula that are defined in its environment,
+replacing the environment with its parent.
+}
+\examples{
+n <- 100
+f <- ~ x + n
+f_unwrap(f)
+}
+
diff --git a/man/function_new.Rd b/man/function_new.Rd
new file mode 100644
index 0000000..768dafe
--- /dev/null
+++ b/man/function_new.Rd
@@ -0,0 +1,40 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/function.R
+\name{function_new}
+\alias{function_new}
+\title{Create a function by "hand"}
+\usage{
+function_new(args, body, env = parent.frame())
+}
+\arguments{
+\item{args}{A named list of default arguments.  Note that if you want
+arguments that don't have defaults, you'll need to use the special function
+\code{\link{alist}}, e.g. \code{alist(a = , b = 1)}}
+
+\item{body}{A language object representing the code inside the function.
+Usually this will be most easily generated with \code{\link{quote}}}
+
+\item{env}{The parent environment of the function, defaults to the calling
+environment of \code{make_function}}
+}
+\description{
+This constructs a new function given it's three components:
+list of arguments, body code and parent environment.
+}
+\examples{
+f <- function(x) x + 3
+g <- function_new(alist(x = ), quote(x + 3))
+
+# The components of the functions are identical
+identical(formals(f), formals(g))
+identical(body(f), body(g))
+identical(environment(f), environment(g))
+
+# But the functions are not identical because f has src code reference
+identical(f, g)
+
+attr(f, "srcref") <- NULL
+# Now they are:
+stopifnot(identical(f, g))
+}
+
diff --git a/man/interp.Rd b/man/interp.Rd
new file mode 100644
index 0000000..d5a828b
--- /dev/null
+++ b/man/interp.Rd
@@ -0,0 +1,47 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/lazy-interp.R
+\name{interp}
+\alias{interp}
+\title{Interpolate values into an expression.}
+\usage{
+interp(`_obj`, ..., .values)
+}
+\arguments{
+\item{_obj}{An object to modify: can be a call, name, formula,
+\code{\link{lazy}}, or a string.}
+
+\item{..., .values}{Either individual name-value pairs, or a list
+(or environment) of values.}
+}
+\description{
+This is useful if you want to build an expression up from a mixture of
+constants and variables.
+}
+\examples{
+# Interp works with formulas, lazy objects, quoted calls and strings
+interp(~ x + y, x = 10)
+interp(lazy(x + y), x = 10)
+interp(quote(x + y), x = 10)
+interp("x + y", x = 10)
+
+# Use as.name if you have a character string that gives a
+# variable name
+interp(~ mean(var), var = as.name("mpg"))
+# or supply the quoted name directly
+interp(~ mean(var), var = quote(mpg))
+
+# Or a function!
+interp(~ f(a, b), f = as.name("+"))
+# Remember every action in R is a function call:
+# http://adv-r.had.co.nz/Functions.html#all-calls
+
+# If you've built up a list of values through some other
+# mechanism, use .values
+interp(~ x + y, .values = list(x = 10))
+
+# You can also interpolate variables defined in the current
+# environment, but this is a little risky.
+y <- 10
+interp(~ x + y, .values = environment())
+}
+
diff --git a/man/is_formula.Rd b/man/is_formula.Rd
new file mode 100644
index 0000000..4bd36c8
--- /dev/null
+++ b/man/is_formula.Rd
@@ -0,0 +1,19 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/formula.R
+\name{is_formula}
+\alias{is_formula}
+\title{Is object a formula?}
+\usage{
+is_formula(x)
+}
+\arguments{
+\item{x}{Object to test}
+}
+\description{
+Is object a formula?
+}
+\examples{
+is_formula(~ 10)
+is_formula(10)
+}
+
diff --git a/man/is_lang.Rd b/man/is_lang.Rd
new file mode 100644
index 0000000..da2de4b
--- /dev/null
+++ b/man/is_lang.Rd
@@ -0,0 +1,46 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/language.R
+\name{is_lang}
+\alias{is_atomic}
+\alias{is_call}
+\alias{is_lang}
+\alias{is_name}
+\alias{is_pairlist}
+\title{Is an object a language object?}
+\usage{
+is_lang(x)
+
+is_name(x)
+
+is_call(x)
+
+is_pairlist(x)
+
+is_atomic(x)
+}
+\arguments{
+\item{x}{An object to test.}
+}
+\description{
+These helpers are consistent wrappers around their base R equivalents.
+A language object is either an atomic vector (typically a scalar), a
+name (aka a symbol), a call, or a pairlist (used for function arguments).
+}
+\examples{
+q1 <- quote(1)
+is_lang(q1)
+is_atomic(q1)
+
+q2 <- quote(x)
+is_lang(q2)
+is_name(q2)
+
+q3 <- quote(x + 1)
+is_lang(q3)
+is_call(q3)
+}
+\seealso{
+\code{\link{as_name}()} and \code{\link{as_call}()} for coercion
+  functions.
+}
+
diff --git a/man/lazy_.Rd b/man/lazy_.Rd
new file mode 100644
index 0000000..87ba54d
--- /dev/null
+++ b/man/lazy_.Rd
@@ -0,0 +1,58 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/lazy.R
+\name{lazy_}
+\alias{lazy}
+\alias{lazy_}
+\title{Capture expression for later lazy evaluation.}
+\usage{
+lazy_(expr, env)
+
+lazy(expr, env = parent.frame(), .follow_symbols = TRUE)
+}
+\arguments{
+\item{expr}{Expression to capture. For \code{lazy_} must be a name
+or a call.}
+
+\item{env}{Environment in which to evaluate expr.}
+
+\item{.follow_symbols}{If \code{TRUE}, the default, follows promises across
+function calls. See \code{vignette("chained-promises")} for details.}
+}
+\description{
+\code{lazy()} uses non-standard evaluation to turn promises into lazy
+objects; \code{lazy_()} does standard evaluation and is suitable for
+programming.
+}
+\details{
+Use \code{lazy()} like you'd use \code{\link{substitute}()}
+to capture an unevaluated promise. Compared to \code{substitute()} it
+also captures the environment associated with the promise, so that you
+can correctly replay it in the future.
+}
+\examples{
+lazy_(quote(a + x), globalenv())
+
+# Lazy is designed to be used inside a function - you should
+# give it the name of a function argument (a promise)
+f <- function(x = b - a) {
+  lazy(x)
+}
+f()
+f(a + b / c)
+
+# Lazy also works when called from the global environment. This makes
+# easy to play with interactively.
+lazy(a + b / c)
+
+# By default, lazy will climb all the way back to the initial promise
+# This is handy if you have if you have nested functions:
+g <- function(y) f(y)
+h <- function(z) g(z)
+f(a + b)
+g(a + b)
+h(a + b)
+
+# To avoid this behavour, set .follow_symbols = FALSE
+# See vignette("chained-promises") for details
+}
+
diff --git a/man/lazy_dots.Rd b/man/lazy_dots.Rd
new file mode 100644
index 0000000..8e68e55
--- /dev/null
+++ b/man/lazy_dots.Rd
@@ -0,0 +1,47 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/lazy-dots.R
+\name{lazy_dots}
+\alias{lazy_dots}
+\title{Capture ... (dots) for later lazy evaluation.}
+\usage{
+lazy_dots(..., .follow_symbols = FALSE, .ignore_empty = FALSE)
+}
+\arguments{
+\item{...}{Dots from another function}
+
+\item{.follow_symbols}{If \code{TRUE}, the default, follows promises across
+function calls. See \code{vignette("chained-promises")} for details.}
+
+\item{.ignore_empty}{If \code{TRUE}, empty arguments will be ignored.}
+}
+\value{
+A named list of \code{\link{lazy}} expressions.
+}
+\description{
+Capture ... (dots) for later lazy evaluation.
+}
+\examples{
+lazy_dots(x = 1)
+lazy_dots(a, b, c * 4)
+
+f <- function(x = a + b, ...) {
+  lazy_dots(x = x, y = a + b, ...)
+}
+f(z = a + b)
+f(z = a + b, .follow_symbols = TRUE)
+
+# .follow_symbols is off by default because it causes problems
+# with lazy loaded objects
+lazy_dots(letters)
+lazy_dots(letters, .follow_symbols = TRUE)
+
+# You can also modify a dots like a list. Anything on the RHS will
+# be coerced to a lazy.
+l <- lazy_dots(x = 1)
+l$y <- quote(f)
+l[c("y", "x")]
+l["z"] <- list(~g)
+
+c(lazy_dots(x = 1), lazy_dots(f))
+}
+
diff --git a/man/lazy_eval.Rd b/man/lazy_eval.Rd
new file mode 100644
index 0000000..a040258
--- /dev/null
+++ b/man/lazy_eval.Rd
@@ -0,0 +1,31 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/lazy-eval.R
+\name{lazy_eval}
+\alias{lazy_eval}
+\title{Evaluate a lazy expression.}
+\usage{
+lazy_eval(x, data = NULL)
+}
+\arguments{
+\item{x}{A lazy object or a formula.}
+
+\item{data}{Option, a data frame or list in which to preferentially look
+for variables before using the environment associated with the lazy
+object.}
+}
+\description{
+Evaluate a lazy expression.
+}
+\examples{
+f <- function(x) {
+  z <- 100
+  ~ x + z
+}
+z <- 10
+lazy_eval(f(10))
+lazy_eval(f(10), list(x = 100))
+lazy_eval(f(10), list(x = 1, z = 1))
+
+lazy_eval(lazy_dots(a = x, b = z), list(x = 10))
+}
+
diff --git a/man/make_call.Rd b/man/make_call.Rd
new file mode 100644
index 0000000..df53ee8
--- /dev/null
+++ b/man/make_call.Rd
@@ -0,0 +1,34 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/lazy-call.R
+\name{make_call}
+\alias{make_call}
+\title{Make a call with \code{lazy_dots} as arguments.}
+\usage{
+make_call(fun, args)
+}
+\arguments{
+\item{fun}{Function as symbol or quoted call.}
+
+\item{args}{Arguments to function; must be a \code{lazy_dots} object,
+or something \code{\link{as.lazy_dots}()} can coerce..}
+}
+\value{
+A list:
+  \item{env}{The common environment for all elements}
+  \item{expr}{The expression}
+}
+\description{
+In order to exactly replay the original call, the environment must be the
+same for all of the dots. This function circumvents that a little,
+falling back to the \code{\link{baseenv}()} if all environments aren't
+the same.
+}
+\examples{
+make_call(quote(f), lazy_dots(x = 1, 2))
+make_call(quote(f), list(x = 1, y = ~x))
+make_call(quote(f), ~x)
+
+# If no known or no common environment, fails back to baseenv()
+make_call(quote(f), quote(x))
+}
+
diff --git a/man/missing_arg.Rd b/man/missing_arg.Rd
new file mode 100644
index 0000000..1246952
--- /dev/null
+++ b/man/missing_arg.Rd
@@ -0,0 +1,16 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/utils.R
+\name{missing_arg}
+\alias{missing_arg}
+\title{Generate a missing argument.}
+\usage{
+missing_arg()
+}
+\description{
+Generate a missing argument.
+}
+\examples{
+f_interp(~f(x = uq(missing_arg())))
+f_interp(~f(x = uq(NULL)))
+}
+
diff --git a/src/expr.c b/src/expr.c
new file mode 100644
index 0000000..2c7ec1e
--- /dev/null
+++ b/src/expr.c
@@ -0,0 +1,76 @@
+#define R_NO_REMAP
+#include <R.h>
+#include <Rinternals.h>
+#include "utils.h"
+
+SEXP base_promise(SEXP promise, SEXP env) {
+  // recurse until we find the real promise, not a promise of a promise
+  while(TYPEOF(promise) == PROMSXP) {
+    env = PRENV(promise);
+    promise = PREXPR(promise);
+
+    // promise has already been forced so can't go further
+    if (env == R_NilValue)
+      break;
+
+    // If the promise is threaded through multiple functions, we'll
+    // get some symbols along the way. If the symbol is bound to a promise
+    // keep going on up
+    if (TYPEOF(promise) == SYMSXP) {
+      SEXP obj = Rf_findVar(promise, env);
+
+      if (TYPEOF(obj) != PROMSXP)
+        break;
+
+      if (is_lazy_load(obj))
+        break;
+
+      promise = obj;
+    }
+  }
+
+  return promise;
+}
+
+// Return NULL if not a promise or has already been forced
+SEXP base_promise_env(SEXP promise, SEXP env) {
+  if (TYPEOF(promise) != PROMSXP)
+    return R_NilValue;
+
+  // recurse until we find the real promise, not a promise of a promise
+  while(TYPEOF(promise) == PROMSXP) {
+    env = PRENV(promise);
+    promise = PREXPR(promise);
+
+    // promise has already been forced so can't go further
+    if (env == R_NilValue)
+      return R_NilValue;
+
+    // If the promise is threaded through multiple functions, we'll
+    // get some symbols along the way. If the symbol is bound to a promise
+    // keep going on up
+    if (TYPEOF(promise) == SYMSXP) {
+      SEXP obj = Rf_findVar(promise, env);
+
+      if (TYPEOF(obj) != PROMSXP)
+        break;
+
+      if (is_lazy_load(obj))
+        break;
+
+      promise = obj;
+    }
+  }
+
+  return env;
+}
+
+SEXP expr_find_(SEXP name, SEXP env) {
+  SEXP promise = Rf_findVar(name, env);
+  return base_promise(promise, env);
+}
+
+SEXP expr_env_(SEXP name, SEXP env, SEXP env_default) {
+  SEXP promise = Rf_findVar(name, env);
+  return base_promise_env(promise, env);
+}
diff --git a/src/interp.c b/src/interp.c
new file mode 100644
index 0000000..13915ee
--- /dev/null
+++ b/src/interp.c
@@ -0,0 +1,48 @@
+#define R_NO_REMAP
+#include <R.h>
+#include <Rdefines.h>
+#include "utils.h"
+
+SEXP interp_walk(SEXP x, SEXP env, SEXP data)  {
+  if (!Rf_isLanguage(x))
+    return x;
+
+  if (is_call_to(x, "uq")) {
+    SEXP uq_call = PROTECT(Rf_lang3(Rf_install("uq"), CADR(x), data));
+    SEXP res = PROTECT(Rf_eval(uq_call, env));
+    UNPROTECT(2);
+    return res;
+  }
+
+  if (is_call_to(x, "uqf")) {
+    return Rf_eval(x, env);
+  }
+
+  // Recursive case
+  for(SEXP cur = x; cur != R_NilValue; cur = CDR(cur)) {
+    SETCAR(cur, interp_walk(CAR(cur), env, data));
+
+    SEXP nxt = CDR(cur);
+    if (is_call_to(CAR(nxt), "uqs")) {
+      // uqs() does error checking and returns a pair list
+      SEXP args_pl = Rf_eval(CAR(nxt), env);
+
+      // Insert args_pl into existing pairlist of args
+      SEXP last_arg = findLast(args_pl);
+      SETCDR(last_arg, CDR(nxt));
+      SETCDR(cur, args_pl);
+    }
+  }
+  return x;
+}
+
+SEXP interp_(SEXP x, SEXP env, SEXP data) {
+  if (!Rf_isLanguage(x))
+    return x;
+
+  if (!Rf_isEnvironment(env))
+    Rf_error("`env` must be an environment");
+
+  return interp_walk(Rf_duplicate(x), env, data);
+}
+
diff --git a/src/lazy.c b/src/lazy.c
new file mode 100644
index 0000000..98883f8
--- /dev/null
+++ b/src/lazy.c
@@ -0,0 +1,107 @@
+#include <R.h>
+#include <Rdefines.h>
+#include "utils.h"
+
+SEXP promise_as_lazy(SEXP promise, SEXP env, int follow_symbols) {
+  // recurse until we find the real promise, not a promise of a promise
+  while(TYPEOF(promise) == PROMSXP) {
+    if (PRENV(promise) == R_NilValue) {
+      Rf_error("Promise has already been forced");
+    }
+
+    env = PRENV(promise);
+    promise = PREXPR(promise);
+
+    // If the promise is threaded through multiple functions, we'll
+    // get some symbols along the way. If the symbol is bound to a promise
+    // keep going on up
+    if (follow_symbols && TYPEOF(promise) == SYMSXP) {
+      SEXP obj = findVar(promise, env);
+
+      if (obj == R_MissingArg || obj == R_UnboundValue)
+        break;
+
+      if (TYPEOF(obj) == PROMSXP && is_lazy_load(obj))
+        break;
+
+      promise = obj;
+    }
+  }
+
+  // Make named list for output
+  SEXP lazy = PROTECT(allocVector(VECSXP, 2));
+  if (NAMED(promise) < 2)
+    SET_NAMED(promise, 2);
+  SET_VECTOR_ELT(lazy, 0, promise);
+  SET_VECTOR_ELT(lazy, 1, env);
+
+  SEXP names = PROTECT(allocVector(STRSXP, 2));
+  SET_STRING_ELT(names, 0, mkChar("expr"));
+  SET_STRING_ELT(names, 1, mkChar("env"));
+
+  setAttrib(lazy, install("names"), names);
+  setAttrib(lazy, install("class"), PROTECT(mkString("lazy")));
+
+  UNPROTECT(3);
+
+  return lazy;
+}
+
+SEXP make_lazy(SEXP name, SEXP env, SEXP follow_symbols_) {
+  SEXP promise = findVar(name, env);
+  int follow_symbols = asLogical(follow_symbols_);
+
+  return promise_as_lazy(promise, env, follow_symbols);
+}
+
+int is_missing(SEXP x) {
+  return TYPEOF(x) == SYMSXP && x == R_MissingArg;
+}
+
+SEXP make_lazy_dots(SEXP env, SEXP follow_symbols_, SEXP ignore_empty_) {
+  SEXP dots = findVar(R_DotsSymbol, env);
+  int follow_symbols = asLogical(follow_symbols_);
+  int ignore_empty = asLogical(ignore_empty_);
+
+  if (dots == R_MissingArg) {
+    SEXP out = PROTECT(Rf_allocVector(VECSXP, 0));
+    setAttrib(out, install("class"), PROTECT(mkString("lazy_dots")));
+    UNPROTECT(2);
+    return out;
+  }
+
+  // Figure out how many elements in dots
+  int n = 0;
+  for(SEXP nxt = dots; nxt != R_NilValue; nxt = CDR(nxt)) {
+    if (ignore_empty && is_missing(CAR(nxt)))
+      continue;
+
+    n++;
+  }
+
+  // Allocate list to store results
+  SEXP lazy_dots = PROTECT(allocVector(VECSXP, n));
+  SEXP names = PROTECT(allocVector(STRSXP, n));
+
+  // Iterate through all elements of dots, converting promises into lazy exprs
+  int i = 0;
+  for(SEXP nxt = dots; nxt != R_NilValue; nxt = CDR(nxt)) {
+    SEXP promise = CAR(nxt);
+
+    if (ignore_empty && is_missing(promise))
+      continue;
+
+    SEXP lazy = promise_as_lazy(promise, env, follow_symbols);
+    SET_VECTOR_ELT(lazy_dots, i, lazy);
+    if (TAG(nxt) != R_NilValue)
+      SET_STRING_ELT(names, i, PRINTNAME(TAG(nxt)));
+
+    i++;
+  }
+  setAttrib(lazy_dots, install("names"), names);
+  setAttrib(lazy_dots, install("class"), PROTECT(mkString("lazy_dots")));
+
+  UNPROTECT(3);
+
+  return lazy_dots;
+}
diff --git a/src/name.c b/src/name.c
new file mode 100644
index 0000000..0056de8
--- /dev/null
+++ b/src/name.c
@@ -0,0 +1,55 @@
+#define R_NO_REMAP
+#include <R.h>
+#include <Rinternals.h>
+#include "utils.h"
+
+// Returns a CHARSXP
+SEXP as_name(SEXP x) {
+  switch(TYPEOF(x)) {
+  case STRSXP:
+    if (Rf_length(x) != 1)
+      Rf_errorcall(R_NilValue, "LHS must evaluate to a single string");
+    return STRING_ELT(x, 0);
+  case SYMSXP:
+    return PRINTNAME(x);
+  case LANGSXP:
+    if (!is_formula(x) || Rf_length(x) != 2)
+      Rf_errorcall(R_NilValue, "RHS of LHS must be a single-sided formula");
+
+    return as_name(rhs(x));
+  default:
+    Rf_errorcall(R_NilValue, "LHS must evaluate to a string or name");
+  }
+}
+
+SEXP lhs_name(SEXP x) {
+  if (TYPEOF(x) != VECSXP)
+    Rf_errorcall(R_NilValue, "`x` must be a list (not a %s)", Rf_type2char(TYPEOF(x)));
+
+  int n = Rf_length(x);
+  SEXP x2 = PROTECT(Rf_shallow_duplicate(x));
+
+  SEXP names = Rf_getAttrib(x2, R_NamesSymbol);
+  if (names == R_NilValue) {
+    names = Rf_allocVector(STRSXP, n);
+    Rf_setAttrib(x2, R_NamesSymbol, names);
+  }
+
+  for (int i = 0; i < n; ++i) {
+    SEXP xi = VECTOR_ELT(x2, i);
+    if (!is_formula(xi) || Rf_length(xi) != 3)
+      continue;
+
+    // set name
+    SEXP name = PROTECT(Rf_eval(lhs(xi), env(xi)));
+    if (TYPEOF(name) != NILSXP)
+      SET_STRING_ELT(names, i, as_name(name));
+    UNPROTECT(1);
+
+    // replace with RHS of formula
+    SET_VECTOR_ELT(x2, i, make_formula1(CADDR(xi), env(xi)));
+  }
+
+  UNPROTECT(1);
+  return x2;
+}
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..29a8e12
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,74 @@
+#define R_NO_REMAP
+#include <R.h>
+#include <Rinternals.h>
+#include <stdbool.h>
+
+bool is_call_to(SEXP x, const char* f) {
+  if (!Rf_isLanguage(x))
+    return false;
+
+  SEXP fun = CAR(x);
+  if (!Rf_isSymbol(fun))
+    return false;
+
+  return fun == Rf_install(f);
+}
+
+bool is_lazy_load(SEXP x) {
+  if (TYPEOF(x) != PROMSXP)
+    return false;
+
+  return is_call_to(PREXPR(x), "lazyLoadDBfetch");
+}
+
+SEXP findLast(SEXP x) {
+  SEXP cons = x;
+  while(CDR(cons) != R_NilValue)
+    cons = CDR(cons);
+
+  return cons;
+}
+
+// Formulas --------------------------------------------------------------------
+
+bool is_formula(SEXP x) {
+  return TYPEOF(x) == LANGSXP && Rf_inherits(x, "formula");
+}
+
+SEXP rhs(SEXP f) {
+  if (!is_formula(f))
+    Rf_errorcall(R_NilValue, "`x` is not a formula");
+
+  switch (Rf_length(f)) {
+  case 2: return CADR(f);
+  case 3: return CADDR(f);
+  default: Rf_errorcall(R_NilValue, "Invalid formula");
+  }
+}
+
+SEXP lhs(SEXP f) {
+  if (!is_formula(f))
+    Rf_errorcall(R_NilValue, "`x` is not a formula");
+
+  switch (Rf_length(f)) {
+  case 2: return R_NilValue;
+  case 3: return CADR(f);
+  default: Rf_errorcall(R_NilValue, "Invalid formula");
+  }
+}
+
+SEXP env(SEXP f) {
+  if (!is_formula(f))
+    Rf_errorcall(R_NilValue, "`x` is not a formula");
+
+  return Rf_getAttrib(f, Rf_install(".Environment"));
+}
+
+SEXP make_formula1(SEXP rhs, SEXP env) {
+  SEXP f = PROTECT(Rf_lang2(Rf_install("~"), rhs));
+  Rf_setAttrib(f, R_ClassSymbol, Rf_mkString("formula"));
+  Rf_setAttrib(f, Rf_install(".Environment"), env);
+
+  UNPROTECT(1);
+  return f;
+}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..6532d5a
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,13 @@
+#define R_NO_REMAP
+#include <stdbool.h>
+#include <R.h>
+#include <Rinternals.h>
+
+bool is_lazy_load(SEXP x);
+bool is_call_to(SEXP x, const char* f);
+bool is_formula(SEXP x);
+SEXP rhs(SEXP f);
+SEXP lhs(SEXP f);
+SEXP env(SEXP f);
+SEXP findLast(SEXP x);
+SEXP make_formula1(SEXP rhs, SEXP env);
diff --git a/tests/testthat.R b/tests/testthat.R
new file mode 100644
index 0000000..d4c8bac
--- /dev/null
+++ b/tests/testthat.R
@@ -0,0 +1,4 @@
+library(testthat)
+library(lazyeval)
+
+test_check("lazyeval")
diff --git a/tests/testthat/ast-irregular.txt b/tests/testthat/ast-irregular.txt
new file mode 100644
index 0000000..9260b3e
--- /dev/null
+++ b/tests/testthat/ast-irregular.txt
@@ -0,0 +1,4 @@
+┗ ()
+ ┗ `foo
+ ┗ <list>
+ ┗ <integer> 
diff --git a/tests/testthat/ast-sample.txt b/tests/testthat/ast-sample.txt
new file mode 100644
index 0000000..847250d
--- /dev/null
+++ b/tests/testthat/ast-sample.txt
@@ -0,0 +1,25 @@
+┗  1
+
+┗ `x
+
+┗ ()
+ ┗ `+
+ ┗ `a
+ ┗ `b
+
+┗ ()
+ ┗ `function
+ ┗ []
+  ┗ x = 1
+  ┗ y =()
+   ┗ `+
+   ┗ `a
+   ┗ `b
+  ┗ z =`MISSING
+ ┗ ()
+  ┗ `{
+  ┗ ()
+   ┗ `+
+   ┗ `c
+   ┗ `d
+ ┗ <srcref> 
diff --git a/tests/testthat/test-ast.R b/tests/testthat/test-ast.R
new file mode 100644
index 0000000..8c37cd1
--- /dev/null
+++ b/tests/testthat/test-ast.R
@@ -0,0 +1,27 @@
+context("ast")
+
+test_that("common cases are as expected", {
+  skip_on_cran() # because of unicode comparison problems.
+
+  x <- list(
+    1,
+    quote(x),
+    quote(a + b),
+    quote(function(x = 1, y = a + b, z) {
+      c + d
+    })
+  )
+
+  expect_output_file(ast_(x), "ast-sample.txt", update = TRUE)
+})
+
+
+test_that("can print trees that can't be generated from text source", {
+  skip_on_cran() # because of unicode comparison problems.
+
+  x <- quote(foo())
+  x[[2]] <- mtcars
+  x[[3]] <- 1:10
+
+  expect_output_file(ast_(x), "ast-irregular.txt", update = TRUE)
+})
diff --git a/tests/testthat/test-call.R b/tests/testthat/test-call.R
new file mode 100644
index 0000000..3923114
--- /dev/null
+++ b/tests/testthat/test-call.R
@@ -0,0 +1,44 @@
+context("call")
+
+# Creation ----------------------------------------------------------------
+
+test_that("character vector must be length 1", {
+  expect_error(call_new(letters), "must be length 1")
+})
+
+test_that("args can be specified individually or as list", {
+  out <- call_new("f", a = 1, .args = list(b = 2))
+  expect_equal(out, quote(f(a = 1, b = 2)))
+})
+
+# Standardisation ---------------------------------------------------------
+
+test_that("can standardise base function", {
+  out <- call_standardise(quote(matrix(nro = 3, 1:9)))
+  expect_equal(out, quote(matrix(data = 1:9, nrow = 3)))
+})
+
+test_that("can standardise local function", {
+  foo <- function(bar, baz) {}
+  out <- call_standardise(quote(foo(baz = 1, 4)))
+  expect_equal(out, quote(foo(bar = 4, baz = 1)))
+})
+
+# Modification ------------------------------------------------------------
+
+test_that("all args must be named", {
+  call <- quote(matrix(1:10))
+  expect_error(call_modify(call, list(1)), "must be named")
+})
+
+test_that("new args inserted at end", {
+  call <- quote(matrix(1:10))
+  out <- call_modify(call, list(nrow = 3))
+  expect_equal(out, quote(matrix(data = 1:10, nrow = 3)))
+})
+
+test_that("new args replace old", {
+  call <- quote(matrix(1:10))
+  out <- call_modify(call, list(data = 3))
+  expect_equal(out, quote(matrix(data = 3)))
+})
diff --git a/tests/testthat/test-complain.R b/tests/testthat/test-complain.R
new file mode 100644
index 0000000..7f10640
--- /dev/null
+++ b/tests/testthat/test-complain.R
@@ -0,0 +1,39 @@
+context("complain")
+
+test_that("NULL return unchanged", {
+  expect_identical(complain(NULL), NULL)
+})
+
+test_that("can't access non-existent list members", {
+  x1 <- list(y = 1)
+  x2 <- complain(x1)
+
+  expect_equal(x2$y, 1)
+  expect_error(x2$z, "object 'z' not found")
+  expect_error(x2[["z"]], "object 'z' not found")
+})
+
+test_that("can't access non-existent environment components", {
+  x1 <- list2env(list(y = 1))
+  x2 <- complain(x1)
+
+  expect_equal(x2$y, 1)
+  expect_error(x2$z, "object 'z' not found")
+  expect_error(x2[["z"]], "object 'z' not found")
+})
+
+test_that("can't use non-character vectors", {
+  x <- complain(list(y = 1))
+
+  expect_error(x[[1]], "subset with a string")
+  expect_error(x[[c("a", "b")]], "subset with a string")
+})
+
+test_that("complain doesn't taint env class", {
+  x1 <- list2env(list(y = 1))
+  x2 <- complain(x1)
+
+  expect_equal(class(x1), "environment")
+  expect_equal(class(x2), c("complain", "environment"))
+
+})
diff --git a/tests/testthat/test-dots.R b/tests/testthat/test-dots.R
new file mode 100644
index 0000000..2169715
--- /dev/null
+++ b/tests/testthat/test-dots.R
@@ -0,0 +1,21 @@
+
+context("lazy_dots")
+
+test_that("lazy_dots works with no args", {
+  l1 <- lazy_dots()
+  l2 <- lazy_dots(.follow_symbols = TRUE)
+
+  expect_equal(l1, structure(list(), class = "lazy_dots"))
+  expect_equal(l2, structure(list(), class = "lazy_dots"))
+
+})
+
+
+test_that(".ignore_empty drops empty arguments", {
+  l1 <- lazy_dots(, 1,)
+  l2 <- lazy_dots(, 1, , .ignore_empty = TRUE)
+
+  expect_equal(length(l1), 3)
+  expect_equal(length(l2), 1)
+  expect_equal(l2[[1]]$expr, 1)
+})
diff --git a/tests/testthat/test-expr.R b/tests/testthat/test-expr.R
new file mode 100644
index 0000000..290cc41
--- /dev/null
+++ b/tests/testthat/test-expr.R
@@ -0,0 +1,80 @@
+context("expr")
+
+# expr_find ---------------------------------------------------------------
+
+test_that("doesn't go pass lazy loaded objects", {
+  expect_identical(expr_find(mtcars), quote(mtcars))
+})
+
+test_that("follows multiple promises", {
+  f <- function(x) g(x)
+  g <- function(y) h(y)
+  h <- function(z) expr_find(z)
+
+  expect_identical(f(x + y), quote(x + y))
+})
+
+
+# expr_env ----------------------------------------------------------------
+
+test_that("follows multiple promises", {
+  f <- function(x) g(x)
+  g <- function(y) h(y)
+  h <- function(z) expr_env(z)
+
+  expect_identical(h(x + y), environment())
+})
+
+test_that("throws error if promise forced", {
+  f <- function(x) {
+    force(x)
+    expr_env(x)
+  }
+  expect_error(f(10), "already been forced")
+})
+
+
+test_that("or can return default env", {
+  env <- new.env(parent = emptyenv())
+  f <- function(x) {
+    force(x)
+    expr_env(x, env)
+  }
+  expect_identical(f(10), env)
+})
+
+# expr_text ---------------------------------------------------------------
+
+test_that("always returns single string", {
+  out <- expr_text({
+    a + b
+  })
+  expect_length(out, 1)
+})
+
+test_that("can truncate lines", {
+  out <- expr_text({
+    a + b
+  }, nlines = 2)
+  expect_equal(out, "{\n...")
+})
+
+# expr_label --------------------------------------------------------------
+
+test_that("quotes strings", {
+  expect_equal(expr_label("a"), '"a"')
+  expect_equal(expr_label("\n"), '"\\n"')
+})
+
+test_that("backquotes names", {
+  expect_equal(expr_label(x), "`x`")
+})
+
+test_that("converts atomics to strings", {
+  expect_equal(expr_label(0.5), "0.5")
+})
+
+test_that("truncates long calls", {
+  expect_equal(expr_label({ a + b }), "`{\n    ...\n}`")
+})
+
diff --git a/tests/testthat/test-f-capture.R b/tests/testthat/test-f-capture.R
new file mode 100644
index 0000000..78b057e
--- /dev/null
+++ b/tests/testthat/test-f-capture.R
@@ -0,0 +1,28 @@
+context("f_capture")
+
+test_that("explicit promise makes a formula", {
+  f1 <- f_capture(1 + 2 + 3)
+  f2 <- ~ 1 + 2 + 3
+
+  expect_equal(f1, f2)
+})
+
+test_that("explicit promise works several levels deep", {
+  f <- function(x) g(x)
+  g <- function(y) h(y)
+  h <- function(z) f_capture(z)
+
+  f1 <- f(1 + 2 + 3)
+  f2 <- ~ 1 + 2 + 3
+
+  expect_equal(f1, f2)
+})
+
+test_that("explicit dots makes a list of formulas", {
+  fs <- dots_capture(x = 1 + 2, y = 2 + 3)
+  f1 <- ~ 1 + 2
+  f2 <- ~ 2 + 3
+
+  expect_equal(fs$x, f1)
+  expect_equal(fs$y, f2)
+})
diff --git a/tests/testthat/test-f-eval.R b/tests/testthat/test-f-eval.R
new file mode 100644
index 0000000..3a63e50
--- /dev/null
+++ b/tests/testthat/test-f-eval.R
@@ -0,0 +1,83 @@
+context("f_eval")
+
+test_that("first argument must be a function", {
+  expect_error(f_eval(10), "`f` is not a formula")
+})
+
+test_that("f_eval uses formula's environment", {
+  x <- 10
+  f <- local({
+    y <- 100
+    ~ x + y
+  })
+
+  expect_equal(f_eval(f), 110)
+})
+
+test_that("data needs to be a list", {
+  expect_error(f_eval(~ x, 10), "Do not know how to find data")
+})
+
+test_that("looks first in `data`", {
+  x <- 10
+  data <- list(x = 100)
+  expect_equal(f_eval(~ x, data), 100)
+})
+
+test_that("pronouns resolve ambiguity looks first in `data`", {
+  x <- 10
+  data <- list(x = 100)
+  expect_equal(f_eval(~ .data$x, data), 100)
+  expect_equal(f_eval(~ .env$x, data), 10)
+})
+
+test_that("pronouns complain about missing values", {
+  expect_error(f_eval(~ .data$x, list()), "Variable 'x' not found in data")
+  expect_error(f_eval(~ .env$`__`, list()), "Object '__' not found in environment")
+})
+
+test_that("f_eval does quasiquoting", {
+  x <- 10
+  expect_equal(f_eval(~ uq(quote(x))), 10)
+})
+
+
+test_that("unquoted formulas look in their own env", {
+  f <- function() {
+    n <- 100
+    ~ n
+  }
+
+  n <- 10
+  expect_equal(f_eval(~ uq(f())), 10)
+})
+
+test_that("unquoted formulas can use data", {
+  f1 <- function() {
+    z <- 100
+    ~ x + z
+  }
+  f2 <- function() {
+    z <- 100
+    ~ .data$x + .env$z
+  }
+
+  z <- 10
+  expect_equal(f_eval(~ uq(f1()), data = list(x = 1)), 101)
+  expect_equal(f_eval(~ uq(f2()), data = list(x = 1)), 101)
+})
+
+test_that("f_eval_lhs uses lhs", {
+  f <- 1 ~ 2
+
+  expect_equal(f_eval_lhs(f), 1)
+})
+
+
+# find_data ---------------------------------------------------------------
+
+test_that("find data works for NULL, lists, and data frames", {
+  expect_equal(find_data(NULL), list())
+  expect_equal(find_data(list(x = 1)), list(x = 1))
+  expect_equal(find_data(mtcars), mtcars)
+})
diff --git a/tests/testthat/test-f-interp.R b/tests/testthat/test-f-interp.R
new file mode 100644
index 0000000..65f28f6
--- /dev/null
+++ b/tests/testthat/test-f-interp.R
@@ -0,0 +1,68 @@
+context("f_interp")
+
+test_that("protected against bad inputs", {
+  f <- ~ x + 1
+  attr(f, ".Environment") <- 10
+  expect_error(f_interp(f), "must be an environment")
+})
+
+test_that("interp produces single string for character inputs", {
+  x <- interp("aaaaaaaaaaaaaa + bbbbbbbbbbbbbbb + ccccccccccccccccc + dddddddddddddddd + eeeeeeeeeeeeeee")
+  expect_is(x, "character")
+  expect_equal(length(x), 1)
+})
+
+
+test_that("can interpolate from environment", {
+  env <- new.env(parent = emptyenv())
+  env$a <- 10
+
+  out <- interp(~ f(a), .values = env)
+  expect_identical(out, ~f(10))
+})
+
+
+# uq ----------------------------------------------------------------------
+
+test_that("evaluates contents of uq()", {
+  expect_equal(f_interp(~ uq(1 + 2)), ~ 3)
+})
+
+
+test_that("unquoted formulas are interpolated first", {
+  f <- function(n) {
+    ~ x + uq(n)
+  }
+  n <- 100
+
+  expect_equal(f_interp(~ uq(f(10))), ~ x + 10)
+})
+
+
+# uqs ---------------------------------------------------------------------
+
+test_that("contents of uqs() must be a vector", {
+  expr <- ~ 1 + uqs(environment())
+  expect_error(f_interp(expr), "`x` must be a vector")
+})
+
+test_that("values of uqs() spliced into expression", {
+  expr <- ~ f(a, uqs(list(quote(b), quote(c))), d)
+  expect_identical(f_interp(expr), ~ f(a, b, c, d))
+})
+
+test_that("names within uqs() are preseved", {
+  expr <- ~ f(uqs(list(a = quote(b))))
+  expect_identical(f_interp(expr), ~ f(a = b))
+})
+
+
+# uqf ---------------------------------------------------------------------
+
+test_that("requires formula", {
+  expect_error(f_interp(~ uqf(10)), "must be a formula")
+})
+
+test_that("interpolates formula", {
+  expect_equal(f_interp(~ uqf(x ~ y)), ~ (x ~ y))
+})
diff --git a/tests/testthat/test-f-list.R b/tests/testthat/test-f-list.R
new file mode 100644
index 0000000..2bba1f0
--- /dev/null
+++ b/tests/testthat/test-f-list.R
@@ -0,0 +1,51 @@
+context("f_list")
+
+test_that("input must be a list", {
+  expect_error(as_f_list(1), "must be a list")
+})
+
+test_that("LHS must evaluate to a string", {
+  expect_error(f_list(1 ~ x), "must evaluate to a string or name")
+  expect_error(f_list(letters ~ x), "must evaluate to a single string")
+  expect_error(f_list(x ~ x ~ z), "must be a single-sided formula")
+})
+
+test_that("regular elements are left as is", {
+  expect_equal(f_list(x = 1:10), list(x = 1:10))
+  expect_equal(f_list(x = ~x), list(x = ~x))
+})
+
+test_that("output is actually a formula", {
+  out <- f_list(x = ~x)[[1]]
+  expect_s3_class(out, "formula")
+  expect_identical(attr(out, ".Environment"), environment())
+})
+
+test_that("output always has names", {
+  out <- f_list(1, 2, 3)
+  expect_equal(names(out), c("", "", ""))
+})
+
+test_that("names taken from LHS of formula", {
+  out1 <- f_list("x" ~ y)
+  out2 <- f_list(quote(x) ~ y)
+
+  var <- ~x
+  out3 <- f_list(var ~ y); out3
+
+  expect_equal(out1, list(x = ~y))
+  expect_equal(out2, list(x = ~y))
+  expect_equal(out3, list(x = ~y))
+})
+
+test_that("null LHS leaves names unchanged", {
+  expect_equal(f_list(x = NULL ~ y), list(x = ~y))
+})
+
+test_that("LHS evaluated in formula environment", {
+  f <- function(x) {
+    paste0(x, 1) ~ y
+  }
+
+  expect_equal(f_list(f("y")), list(y1 = ~ y))
+})
diff --git a/tests/testthat/test-f-unwrap.R b/tests/testthat/test-f-unwrap.R
new file mode 100644
index 0000000..520962d
--- /dev/null
+++ b/tests/testthat/test-f-unwrap.R
@@ -0,0 +1,19 @@
+context("f_unwrap")
+
+test_that("f_unwrap substitutes values", {
+  n <- 100
+  f1 <- f_unwrap(~ x + n)
+  f2 <- f_new(quote(x + 100), env = parent.env(environment()))
+
+  expect_identical(f1, f2)
+})
+
+test_that("f_unwrap substitutes even in globalenv", {
+  .GlobalEnv$`__1` <- 1
+  expect_equal(f_rhs(f_unwrap(f_new(quote(`__1`), env = globalenv()))), 1)
+})
+
+test_that("doesn't go past empty env", {
+  f <- f_new(quote(x == y), env = emptyenv())
+  expect_equal(f_unwrap(f), f)
+})
diff --git a/tests/testthat/test-formula.R b/tests/testthat/test-formula.R
new file mode 100644
index 0000000..e8f42a3
--- /dev/null
+++ b/tests/testthat/test-formula.R
@@ -0,0 +1,73 @@
+context("formula")
+
+# Creation ----------------------------------------------------------------
+
+test_that("expr must be valid type", {
+  expect_error(f_new(list()), "must be a language object")
+  expect_error(f_new(quote(a), list()), "must be a language object")
+  expect_error(f_new(quote(a), env = list()), "must be an environment")
+})
+
+test_that("equivalent to ~", {
+  f1 <- ~abc
+  f2 <- f_new(quote(abc))
+
+  expect_identical(f1, f2)
+})
+
+test_that("is_formula works", {
+  expect_true(is_formula(~10))
+  expect_false(is_formula(10))
+})
+
+# Getters -----------------------------------------------------------------
+
+test_that("throws errors for bad inputs", {
+  expect_error(f_rhs(1), "not a formula")
+  expect_error(f_rhs(`~`()), "Invalid formula")
+  expect_error(f_rhs(`~`(1, 2, 3)), "Invalid formula")
+
+  expect_error(f_lhs(1), "not a formula")
+  expect_error(f_lhs(`~`()), "Invalid formula")
+  expect_error(f_lhs(`~`(1, 2, 3)), "Invalid formula")
+
+  expect_error(f_env(1), "not a formula")
+})
+
+test_that("extracts call, name, or scalar", {
+  expect_identical(f_rhs(~ x), quote(x))
+  expect_identical(f_rhs(~ f()), quote(f()))
+  expect_identical(f_rhs(~ 1L), 1L)
+})
+
+# Setters -----------------------------------------------------------------
+
+test_that("can replace RHS of one-sided formula", {
+  f <- ~ x1
+  f_rhs(f) <- quote(x2)
+
+  expect_equal(f, ~ x2)
+})
+
+test_that("can replace both sides of two-sided formula", {
+  f <- x1 ~ y1
+  f_lhs(f) <- quote(x2)
+  f_rhs(f) <- quote(y2)
+
+  expect_equal(f, x2 ~ y2)
+})
+
+test_that("can remove lhs of two-sided formula", {
+  f <- x ~ y
+  f_lhs(f) <- NULL
+
+  expect_equal(f, ~ y)
+})
+
+test_that("can modify environment", {
+  f <- x ~ y
+  env <- new.env()
+  f_env(f) <- env
+
+  expect_equal(f_env(f), env)
+})
diff --git a/tests/testthat/test-function.R b/tests/testthat/test-function.R
new file mode 100644
index 0000000..fd564f1
--- /dev/null
+++ b/tests/testthat/test-function.R
@@ -0,0 +1,12 @@
+context("function")
+
+test_that("function_new equivalent to regular function", {
+  f1 <- function(x = a + b, y) {
+    x + y
+  }
+  attr(f1, "srcref") <- NULL
+
+  f2 <- function_new(alist(x = a + b, y =), quote({x + y}))
+
+  expect_equal(f1, f2)
+})
diff --git a/tests/testthat/test-language.R b/tests/testthat/test-language.R
new file mode 100644
index 0000000..ed30db7
--- /dev/null
+++ b/tests/testthat/test-language.R
@@ -0,0 +1,26 @@
+context("language")
+
+test_that("NULL is a valid language object", {
+  expect_true(is_lang(NULL))
+})
+
+# coercion ----------------------------------------------------------------
+
+test_that("as_name produces names", {
+  expect_equal(as_name("a"), quote(a))
+  expect_equal(as_name(quote(a)), quote(a))
+  expect_equal(as_name(quote(a())), quote(a))
+  expect_equal(as_name(~ a), quote(a))
+  expect_equal(as_name(~ a()), quote(a))
+
+  expect_error(as_name(c("a", "b")), "Can not coerce character vector of length > 1")
+})
+
+test_that("as_call produces calls", {
+  expect_equal(as_call(quote(a)), quote(a()))
+  expect_equal(as_call(quote(a())), quote(a()))
+  expect_equal(as_call("a()"), quote(a()))
+  expect_equal(as_call(~ a()), quote(a()))
+
+  expect_error(as_call(c("a", "b")), "Can not coerce character vector of length > 1")
+})
diff --git a/tests/testthat/test-lazy.R b/tests/testthat/test-lazy.R
new file mode 100644
index 0000000..5d73734
--- /dev/null
+++ b/tests/testthat/test-lazy.R
@@ -0,0 +1,47 @@
+context("lazy")
+
+
+lazy_caller <- function(arg) {
+  lazy(arg)
+}
+outer_fun <- function(arg) {
+  lazy_caller(arg)
+}
+
+
+test_that("basic lazy() functionality works", {
+  expect_equal(lazy_caller(0)$expr, 0)
+  expect_equal(lazy_caller("char")$expr, "char")
+  expect_equal(lazy_caller(sym)$expr, as.name("sym"))
+  expect_equal(lazy_caller(call("name"))$expr, quote(call("name")))
+})
+
+test_that("lazy() works with nested promises", {
+  expect_equal(outer_fun(0)$expr, 0)
+  expect_equal(outer_fun("char")$expr, "char")
+  expect_equal(outer_fun(sym)$expr, as.name("sym"))
+  expect_equal(outer_fun(call("name"))$expr, quote(call("name")))
+})
+
+test_that("lazy() does not unpack lazily loaded objects", {
+  lazy <- lazy_caller(mean)
+  expect_equal(deparse(lazy$expr), "mean")
+
+  nested_lazy <- outer_fun(mean)
+  expect_equal(deparse(lazy$expr), "mean")
+
+  outer_fun2 <- function() {
+    list(
+      lazy = lazy_caller(mean),
+      env = environment()
+    )
+  }
+  embedded_lazy <- outer_fun2()
+  expect_identical(embedded_lazy$lazy$expr, as.name("mean"))
+  expect_identical(embedded_lazy$lazy$env, embedded_lazy$env)
+})
+
+test_that("lazy() works for double-colon operator", {
+  expect_error(lazy <- lazy_caller(stats::runif(10)), NA)
+  expect_error(nested_lazy <- outer_fun(stats::runif(10)), NA)
+})
diff --git a/tests/testthat/test-names.R b/tests/testthat/test-names.R
new file mode 100644
index 0000000..a5cb28f
--- /dev/null
+++ b/tests/testthat/test-names.R
@@ -0,0 +1,8 @@
+context("names")
+
+test_that("auto_name does not truncate symbols (#19)", {
+  long_name <- quote(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA)
+  dots <- as.lazy_dots(long_name)
+
+  expect_equal(auto_names(dots), as.character(long_name))
+})
diff --git a/vignettes/lazyeval-old.Rmd b/vignettes/lazyeval-old.Rmd
new file mode 100644
index 0000000..35b0108
--- /dev/null
+++ b/vignettes/lazyeval-old.Rmd
@@ -0,0 +1,212 @@
+---
+title: "Lazyeval: a new approach to NSE"
+date: "`r Sys.Date()`"
+output: rmarkdown::html_vignette
+vignette: >
+  %\VignetteIndexEntry{Lazyeval: a new approach to NSE}
+  %\VignetteEngine{knitr::rmarkdown}
+  %\usepackage[utf8]{inputenc}
+---
+
+```{r, echo = FALSE}
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+rownames(mtcars) <- NULL
+```
+
+This document outlines my previous approach to non-standard evaluation (NSE). You should avoid it unless you are working with an older version of dplyr or tidyr.
+
+There are three key ideas:
+
+* Instead of using `substitute()`, use `lazyeval::lazy()` to capture both expression
+  and environment. (Or use `lazyeval::lazy_dots(...)` to capture promises in `...`)
+  
+* Every function that uses NSE should have a standard evaluation (SE) escape 
+  hatch that does the actual computation. The SE-function name should end with 
+  `_`.
+  
+* The SE-function has a flexible input specification to make it easy for people
+  to program with.
+
+## `lazy()`
+
+The key tool that makes this approach possible is `lazy()`, an equivalent to `substitute()` that captures both expression and environment associated with a function argument:
+
+```{r}
+library(lazyeval)
+f <- function(x = a - b) {
+  lazy(x)
+}
+f()
+f(a + b)
+```
+
+As a complement to `eval()`, the lazy package provides `lazy_eval()` that uses the environment associated with the lazy object:
+
+```{r}
+a <- 10
+b <- 1
+lazy_eval(f())
+lazy_eval(f(a + b))
+```
+
+The second argument to lazy eval is a list or data frame where names should be looked up first:
+
+```{r}
+lazy_eval(f(), list(a = 1))
+```
+
+`lazy_eval()` also works with formulas, since they contain the same information as a lazy object: an expression (only the RHS is used by convention) and an environment:
+
+```{r}
+lazy_eval(~ a + b)
+h <- function(i) {
+  ~ 10 + i
+}
+lazy_eval(h(1))
+```
+
+## Standard evaluation
+
+Whenever we need a function that does non-standard evaluation, always write the standard evaluation version first. For example, let's implement our own version of `subset()`:
+
+```{r}
+subset2_ <- function(df, condition) {
+  r <- lazy_eval(condition, df)
+  r <- r & !is.na(r)
+  df[r, , drop = FALSE]
+} 
+
+subset2_(mtcars, lazy(mpg > 31))
+```
+
+`lazy_eval()` will always coerce it's first argument into a lazy object, so a variety of specifications will work:
+
+```{r}
+subset2_(mtcars, ~mpg > 31)
+subset2_(mtcars, quote(mpg > 31))
+subset2_(mtcars, "mpg > 31")
+```
+
+Note that quoted called and strings don't have environments associated with them, so `as.lazy()` defaults to using `baseenv()`. This will work if the expression is self-contained (i.e. doesn't contain any references to variables in the local environment), and will otherwise fail quickly and robustly.
+
+## Non-standard evaluation
+
+With the SE version in hand, writing the NSE version is easy. We just use `lazy()` to capture the unevaluated expression and corresponding environment:
+
+```{r}
+subset2 <- function(df, condition) {
+  subset2_(df, lazy(condition))
+}
+subset2(mtcars, mpg > 31)
+```
+
+This standard evaluation escape hatch is very important because it allows us to implement different NSE approaches. For example, we could create a subsetting function that finds all rows where a variable is above a threshold:
+
+```{r}
+above_threshold <- function(df, var, threshold) {
+  cond <- interp(~ var > x, var = lazy(var), x = threshold)
+  subset2_(df, cond)
+}
+above_threshold(mtcars, mpg, 31)
+```
+
+Here we're using `interp()` to modify a formula. We use the value of `threshold` and the expression in  by `var`.
+
+## Scoping
+
+Because `lazy()` captures the environment associated with the function argument, we automatically avoid a subtle scoping bug present in `subset()`:
+  
+```{r}
+x <- 31
+f1 <- function(...) {
+  x <- 30
+  subset(mtcars, ...)
+}
+# Uses 30 instead of 31
+f1(mpg > x)
+
+f2 <- function(...) {
+  x <- 30
+  subset2(mtcars, ...)
+}
+# Correctly uses 31
+f2(mpg > x)
+```
+
+`lazy()` has another advantage over `substitute()` - by default, it follows promises across function invocations. This simplifies the casual use of NSE.
+
+```{r, eval = FALSE}
+x <- 31
+g1 <- function(comp) {
+  x <- 30
+  subset(mtcars, comp)
+}
+g1(mpg > x)
+#> Error: object 'mpg' not found
+```
+
+```{r}
+g2 <- function(comp) {
+  x <- 30
+  subset2(mtcars, comp)
+}
+g2(mpg > x)
+```
+
+Note that `g2()` doesn't have a standard-evaluation escape hatch, so it's not suitable for programming with in the same way that `subset2_()` is. 
+
+## Chained promises
+
+Take the following example:
+
+```{r}
+library(lazyeval)
+f1 <- function(x) lazy(x)
+g1 <- function(y) f1(y)
+
+g1(a + b)
+```
+
+`lazy()` returns `a + b` because it always tries to find the top-level promise.
+
+In this case the process looks like this:
+
+1. Find the object that `x` is bound to.
+2. It's a promise, so find the expr it's bound to (`y`, a symbol) and the
+   environment in which it should be evaluated (the environment of `g()`).
+3. Since `x` is bound to a symbol, look up its value: it's bound to a promise.
+4. That promise has expression `a + b` and should be evaluated in the global
+   environment.
+5. The expression is not a symbol, so stop.
+
+Occasionally, you want to avoid this recursive behaviour, so you can use `follow_symbol = FALSE`:
+
+```{r}
+f2 <- function(x) lazy(x, .follow_symbols = FALSE)
+g2 <- function(y) f2(y)
+
+g2(a + b)
+```
+
+Either way, if you evaluate the lazy expression you'll get the same result:
+
+```{r}
+a <- 10
+b <- 1
+
+lazy_eval(g1(a + b))
+lazy_eval(g2(a + b))
+```
+
+Note that the resolution of chained promises only works with unevaluated objects. This is because R deletes the information about the environment associated with a promise when it has been forced, so that the garbage collector is allowed to remove the environment from memory in case it is no longer used. `lazy()` will fail with an error in such situations.
+
+```{r, error = TRUE, purl = FALSE}
+var <- 0
+
+f3 <- function(x) {
+  force(x)
+  lazy(x)
+}
+
+f3(var)
+```
diff --git a/vignettes/lazyeval.Rmd b/vignettes/lazyeval.Rmd
new file mode 100644
index 0000000..c672fe3
--- /dev/null
+++ b/vignettes/lazyeval.Rmd
@@ -0,0 +1,617 @@
+---
+title: "Non-standard evaluation"
+author: "Hadley Wickham"
+date: "`r Sys.Date()`"
+output: rmarkdown::html_vignette
+vignette: >
+  %\VignetteIndexEntry{Non-standard evaluation}
+  %\VignetteEngine{knitr::rmarkdown}
+  %\VignetteEncoding{UTF-8}
+---
+
+```{r, include = FALSE}
+library(lazyeval)
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+```
+
+This document describes lazyeval, a package that provides principled tools to perform non-standard evaluation (NSE) in R. You should read this vignette if you want to program with packages like dplyr and ggplot2[^1], or you want a principled way of working with delayed expressions in your own package. As the name suggests, non-standard evaluation breaks away from the standard evaluation (SE) rules in order to do something special. There are three common uses of NSE:
+
+1.  __Labelling__ enhances plots and tables by using the expressions
+    supplied to a function, rather than their values. For example, note the
+    axis labels in this plot:
+
+    ```{r, fig.width = 4, fig.height = 2.5}
+    par(mar = c(4.5, 4.5, 1, 0.5))
+    grid <- seq(0, 2 * pi, length = 100)
+    plot(grid, sin(grid), type = "l")
+    ```
+
+1.  __Non-standard scoping__ looks for objects in places other than the current
+    environment. For example, base R has `with()`, `subset()`, and `transform()` 
+    that look for objects in a data frame (or list) before the current 
+    environment:
+
+    ```{r}
+    df <- data.frame(x = c(1, 5, 4, 2, 3), y = c(2, 1, 5, 4, 3))
+    
+    with(df, mean(x))
+    subset(df, x == y)
+    transform(df, z = x + y)
+    ```
+
+1.  __Metaprogramming__ is a catch-all term that covers all other uses of 
+    NSE (such as in `bquote()` and `library()`). Metaprogramming is so called 
+    because it involves computing on the unevaluated code in some way.
+
+This document is broadly organised according to the three types of non-standard evaluation described above. The main difference is that after [labelling], we'll take a detour to learn more about [formulas]. You're probably familiar with formulas from linear models (e.g. `lm(mpg ~ displ, data = mtcars)`) but formulas are more than just a tool for modelling: they are a general way of capturing an unevaluated expression. 
+
+The approaches recommended here are quite different to my previous generation of recommendations. I am fairly confident these new approaches are correct, and will not have to change substantially again. The current tools make it easy to solve a number of practical problems that were previously challenging and are rooted in [long-standing theory](http://repository.readscheme.org/ftp/papers/pepm99/bawden.pdf).
+
+[^1]: Currently neither ggplot2 nor dplyr actually use these tools since I've only just figured it out. But I'll be working hard to make sure all my packages are consistent in the near future.
+
+## Labelling
+
+In base R, the classic way to turn an argument into a label is to use `deparse(substitute(x))`:
+
+```{r}
+my_label <- function(x) deparse(substitute(x))
+my_label(x + y)
+```
+
+There are two potential problems with this approach:
+
+1.  For long some expressions, `deparse()` generates a character vector with 
+    length > 1:
+    
+    ```{r}
+    my_label({
+      a + b
+      c + d
+    })
+    ```
+
+1.  `substitute()` only looks one level up, so you lose the original label if 
+    the function isn't called directly:
+    
+    ```{r}
+    my_label2 <- function(x) my_label(x)
+    my_label2(a + b)
+    ```
+
+Both of these problems are resolved by `lazyeval::expr_text()`:
+
+```{r}
+my_label <- function(x) expr_text(x)
+my_label2 <- function(x) my_label(x)
+   
+my_label({
+  a + b
+  c + d
+})
+my_label2(a + b)
+```
+
+There are two variations on the theme of `expr_text()`:
+
+*   `expr_find()` find the underlying expression. It works similarly to 
+    `substitute()` but will follow a chain of promises back up to the original
+    expression. This is often useful for [metaprogramming].
+  
+*   `expr_label()` is a customised version of `expr_text()` that produces 
+    labels designed to be used in messages to the user:
+
+    ```{r}
+    expr_label(x)
+    expr_label(a + b + c)
+    expr_label(foo({
+      x + y
+    }))
+    ```
+
+### Exercises
+
+1.  `plot()` uses `deparse(substitute(x))` to generate labels for the x and y
+    axes. Can you generate input that causes it to display bad labels?
+    Write your own wrapper around `plot()` that uses `expr_label()` to compute
+    `xlim` and `ylim`.
+    
+1.  Create a simple implementation of `mean()` that stops with an informative
+    error message if the argument is not numeric:
+    
+    ```{r, eval = FALSE}
+    x <- c("a", "b", "c")
+    my_mean(x)
+    #> Error: `x` is a not a numeric vector.
+    my_mean(x == "a")
+    #> Error: `x == "a"` is not a numeric vector.
+    my_mean("a")
+    #> Error: "a" is not a numeric vector.
+    ```
+
+1.  Read the source code for `expr_text()`. How does it work? What additional
+    arguments to `deparse()` does it use?
+
+## Formulas
+
+Non-standard scoping is probably the most useful NSE tool, but before we can talk about a solid approach, we need to take a detour to talk about formulas. Formulas are a familiar tool from linear models, but their utility is not limited to models. In fact, formulas are a powerful, general purpose tool, because a formula captures two things: 
+
+1. An unevaluated expression.
+1. The context (environment) in which the expression was created.
+
+`~` is a single character that allows you to say: "I want to capture the meaning of this code, without evaluating it right away". For that reason, the formula can be thought of as a "quoting" operator.
+
+### Definition of a formula
+
+Technically, a formula is a "language" object (i.e. an unevaluated expression) with a class of "formula" and an attribute that stores the environment:
+
+```{r}
+f <- ~ x + y + z
+typeof(f)
+attributes(f)
+```
+
+The structure of the underlying object is slightly different depending on whether you have a one-sided or two-sided formula:
+
+*   One-sided formulas have length two:
+
+    ```{r}
+    length(f)
+    # The 1st element is always ~
+    f[[1]]
+    # The 2nd element is the RHS
+    f[[2]]
+    ```
+
+*   Two-sided formulas have length three:
+
+    ```{r}
+    g <- y ~ x + z
+    length(g)
+    # The 1st element is still ~
+    g[[1]]
+    # But now the 2nd element is the LHS
+    g[[2]]
+    # And the 3rd element is the RHS
+    g[[3]]
+    ```
+
+To abstract away these differences, lazyeval provides `f_rhs()` and `f_lhs()` to access either side of the formula, and `f_env()` to access its environment:
+
+```{r}
+f_rhs(f)
+f_lhs(f)
+f_env(f)
+
+f_rhs(g)
+f_lhs(g)
+f_env(g)
+```
+
+### Evaluating a formula
+
+A formula captures delays the evaluation of an expression so you can later evaluate it with `f_eval()`:
+
+```{r}
+f <- ~ 1 + 2 + 3
+f
+f_eval(f)
+```
+
+This allows you to use a formula as a robust way of delaying evaluation, cleanly separating the creation of the formula from its evaluation. Because formulas capture the code and context, you get the correct result even when a formula is created and evaluated in different places. In the following example, note that the value of `x` inside `add_1000()` is used:
+
+```{r}
+x <- 1
+add_1000 <- function(x) {
+  ~ 1000 + x
+}
+
+add_1000(3)
+f_eval(add_1000(3))
+```
+
+It can be hard to see what's going on when looking at a formula because important values are stored in the environment, which is largely opaque. You can use `f_unwrap()` to replace names with their corresponding values:
+
+```{r}
+f_unwrap(add_1000(3))
+```
+
+### Non-standard scoping
+
+`f_eval()` has an optional second argument: a named list (or data frame) that overrides values found in the formula's environment. 
+
+```{r}
+y <- 100
+f_eval(~ y)
+f_eval(~ y, data = list(y = 10))
+
+# Can mix variables in environment and data argument
+f_eval(~ x + y, data = list(x = 10))
+# Can even supply functions
+f_eval(~ f(y), data = list(f = function(x) x * 3))
+```
+
+This makes it very easy to implement non-standard scoping:
+
+```{r}
+f_eval(~ mean(cyl), data = mtcars)
+```
+
+One challenge with non-standard scoping is that we've introduced some ambiguity. For example, in the code below does `x` come from `mydata` or the environment?
+
+```{r, eval = FALSE}
+f_eval(~ x, data = mydata)
+```
+
+You can't tell without knowing whether or not `mydata` has a variable called `x`. To overcome this problem, `f_eval()` provides two pronouns:
+
+* `.data` is bound to the data frame.
+* `.env` is bound to the formula environment.
+
+They both start with `.` to minimise the chances of clashing with existing variables.
+
+With these pronouns we can rewrite the previous formula to remove the ambiguity:
+
+```{r}
+mydata <- data.frame(x = 100, y = 1)
+x <- 10
+
+f_eval(~ .env$x, data = mydata)
+f_eval(~ .data$x, data = mydata)
+```
+
+If the variable or object doesn't exist, you'll get an informative error:
+
+```{r, error = TRUE}
+f_eval(~ .env$z, data = mydata)
+f_eval(~ .data$z, data = mydata)
+```
+
+### Unquoting
+
+`f_eval()` has one more useful trick up its sleeve: unquoting. Unquoting allows you to write functions where the user supplies part of the formula. For example, the following function allows you to compute the mean of any column (or any function of a column):
+
+```{r}
+df_mean <- function(df, variable) {
+  f_eval(~ mean(uq(variable)), data = df)
+}
+
+df_mean(mtcars, ~ cyl)
+df_mean(mtcars, ~ disp * 0.01638)
+df_mean(mtcars, ~ sqrt(mpg))
+```
+
+To see how this works, we can use `f_interp()` which `f_eval()` calls internally (you shouldn't call it in your own code, but it's useful for debugging). The key is `uq()`: `uq()` evaluates its first (and only) argument and inserts the value into the formula:
+    
+```{r}
+variable <- ~cyl
+f_interp(~ mean(uq(variable)))
+
+variable <- ~ disp * 0.01638
+f_interp(~ mean(uq(variable)))
+```
+
+Unquoting allows you to create code "templates", where you write most of the expression, while still allowing the user to control important components. You can even use `uq()` to change the function being called:
+
+```{r}
+f <- ~ mean
+f_interp(~ uq(f)(uq(variable)))
+```
+
+Note that `uq()` only takes the RHS of a formula, which makes it difficult to insert literal formulas into a call:
+
+```{r}
+formula <- y ~ x
+f_interp(~ lm(uq(formula), data = df))
+```
+
+You can instead use `uqf()` which uses the whole formula, not just the RHS:
+
+```{r}
+f_interp(~ lm(uqf(formula), data = df))
+```
+
+Unquoting is powerful, but it only allows you to modify a single argument: it doesn't allow you to add an arbitrary number of arguments. To do that, you'll need "unquote-splice", or `uqs()`. The first (and only) argument to `uqs()` should be a list of arguments to be spliced into the call:
+
+```{r}
+variable <- ~ x
+extra_args <- list(na.rm = TRUE, trim = 0.9)
+f_interp(~ mean(uq(variable), uqs(extra_args)))
+```
+
+### Exercises
+
+1.  Create a wrapper around `lm()` that allows the user to supply the 
+    response and predictors as two separate formulas.
+    
+1.  Compare and contrast `f_eval()` with `with()`.
+
+1.  Why does this code work even though `f` is defined in two places? (And
+    one of them is not a function).
+
+    ```{r}
+    f <- function(x) x + 1
+    f_eval(~ f(10), list(f = "a"))
+    ```
+
+## Non-standard scoping
+
+Non-standard scoping (NSS) is an important part of R because it makes it easy to write functions tailored for interactive data exploration. These functions require less typing, at the cost of some ambiguity and "magic". This is a good trade-off for interactive data exploration because you want to get ideas out of your head and into the computer as quickly as possible. If a function does make a bad guess, you'll spot it quickly because you're working interactively.
+
+There are three challenges to implementing non-standard scoping:
+
+1.  You must correctly delay the evaluation of a function argument, capturing 
+    both the computation (the expression), and the context (the environment).
+    I recommend making this explicit by requiring the user to "quote" any NSS
+    arguments with `~`, and then evaluating explicit with `f_eval()`.
+  
+1.  When writing functions that use NSS-functions, you need some way to
+    avoid the automatic lookup and be explicit about where objects should be
+    found. `f_eval()` solves this problem with the `.data.` and `.env` 
+    pronouns.
+
+1.  You need some way to allow the user to supply parts of a formula. 
+    `f_eval()` solves this with unquoting.
+
+To illustrate these challenges, I will implement a `sieve()` function that works similarly to `base::subset()` or `dplyr::filter()`. The goal of `sieve()` is to make it easy to select observations that match criteria defined by a logical expression. `sieve()` has three advantages over `[`:
+
+1.  It is much more compact when the condition uses many variables, because 
+    you don't need to repeat the name of the data frame many times.
+
+1.  It drops rows where the condition evaluates to `NA`, rather than filling 
+    them with `NA`s.
+    
+1.  It always returns a data frame.
+
+The implementation of `sieve()` is straightforward. First we use `f_eval()` to perform NSS. Then we then check that we have a logical vector, replace `NA`s with `FALSE`, and subset with `[`.
+
+```{R}
+sieve <- function(df, condition) {
+  rows <- f_eval(condition, df)
+  if (!is.logical(rows)) {
+    stop("`condition` must be logical.", call. = FALSE)
+  }
+  
+  rows[is.na(rows)] <- FALSE
+  df[rows, , drop = FALSE]
+}
+
+df <- data.frame(x = 1:5, y = 5:1)
+sieve(df, ~ x <= 2)
+sieve(df, ~ x == y)
+```
+
+### Programming with `sieve()`
+
+Imagine that you've written some code that looks like this:
+
+```{r, eval = FALSE}
+sieve(march, ~ x > 100)
+sieve(april, ~ x > 50)
+sieve(june, ~ x > 45)
+sieve(july, ~ x > 17)
+```
+
+(This is a contrived example, but it illustrates all of the important issues you'll need to consider when writing more useful functions.)
+
+Instead of continuing to copy-and-paste your code, you decide to wrap up the common behaviour in a function: 
+
+```{r}
+threshold_x <- function(df, threshold) {
+  sieve(df, ~ x > threshold)
+}
+threshold_x(df, 3)
+```
+
+There are two ways that this function might fail:
+
+1.  The data frame might not have a variable called `x`. This will fail unless
+    there's a variable called `x` hanging around in the global environment:
+    
+    ```{r, error = TRUE}
+    rm(x)
+    df2 <- data.frame(y = 5:1)
+    
+    # Throws an error
+    threshold_x(df2, 3)
+    
+    # Silently gives the incorrect result!
+    x <- 5
+    threshold_x(df2, 3)
+    ```
+    
+1.  The data frame might have a variable called `threshold`:
+
+    ```{r}
+    df3 <- data.frame(x = 1:5, y = 5:1, threshold = 4)
+    threshold_x(df3, 3)
+    ```
+
+These failures are partiuclarly pernicious because instead of throwing an error they silently produce the wrong answer. Both failures arise because `f_eval()` introduces ambiguity by looking in two places for each name: the supplied data and formula environment. 
+
+To make `threshold_x()` more reliable, we need to be more explicit by using the `.data` and `.env` pronouns:
+
+```{r, error = TRUE}
+threshold_x <- function(df, threshold) {
+  sieve(df, ~ .data$x > .env$threshold)
+}
+
+threshold_x(df2, 3)
+threshold_x(df3, 3)
+```
+
+Here `.env` is bound to the environment where `~` is evaluated, namely the inside of `threshold_x()`.
+
+### Adding arguments
+
+The `threshold_x()` function is not very useful because it's bound to a specific variable. It would be more powerful if we could vary both the threshold and the variable it applies to. We can do that by taking an additional argument to specify which variable to use. 
+
+One simple approach is to use a string and `[[`:
+
+```{r}
+threshold <- function(df, variable, threshold) {
+  stopifnot(is.character(variable), length(variable) == 1)
+  
+  sieve(df, ~ .data[[.env$variable]] > .env$threshold)
+}
+threshold(df, "x", 4)
+```
+
+This is a simple and robust solution, but only allows us to use an existing variable, not an arbitrary expression like `sqrt(x)`.
+
+A more general solution is to allow the user to supply a formula, and use unquoting:
+
+```{r}
+threshold <- function(df, variable = ~x, threshold = 0) {
+  sieve(df, ~ uq(variable) > .env$threshold)
+}
+
+threshold(df, ~ x, 4)
+threshold(df, ~ abs(x - y), 2)
+```
+
+In this case, it's the responsibility of the user to ensure the `variable` is specified unambiguously. `f_eval()` is designed so that `.data` and `.env` work even when evaluated inside of `uq()`:
+
+```{r}
+x <- 3
+threshold(df, ~ .data$x - .env$x, 0)
+```
+
+### Dot-dot-dot
+
+There is one more tool that you might find useful for functions that take `...`. For example, the code below implements a function similar to `dplyr::mutate()` or `base::transform()`.
+
+```{r}
+mogrify <- function(`_df`, ...) {
+  args <- list(...)
+  
+  for (nm in names(args)) {
+    `_df`[[nm]] <- f_eval(args[[nm]], `_df`)
+  }
+  
+  `_df`
+}
+```
+
+(NB: the first argument is a non-syntactic name (i.e. it requires quoting with `` ` ``) so it doesn't accidentally match one of the names of the new variables.)
+
+`transmogrifty()` makes it easy to add new variables to a data frame:
+
+```{r}
+df <- data.frame(x = 1:5, y = sample(5))
+mogrify(df, z = ~ x + y, z2 = ~ z * 2)
+```
+
+One problem with this implementation is that it's hard to specify the names of the generated variables. Imagine you want a function where the name and expression are in separate variables. This is awkward because the variable name is supplied as an argument name to `mogrify()`:
+
+```{r}
+add_variable <- function(df, name, expr) {
+  do.call("mogrify", c(list(df), setNames(list(expr), name)))
+}
+add_variable(df, "z", ~ x + y)
+```
+
+Lazyeval provides the `f_list()` function to make writing this sort of function a little easier. It takes a list of formulas and evaluates the LHS of each formula (if present) to rename the elements:
+
+```{r}
+f_list("x" ~ y, z = ~z)
+```
+
+If we tweak `mogrify()` to use `f_list()` instead of `list()`:
+
+```{r}
+mogrify <- function(`_df`, ...) {
+  args <- f_list(...)
+  
+  for (nm in names(args)) {
+    `_df`[[nm]] <- f_eval(args[[nm]], `_df`)
+  }
+  
+  `_df`
+}
+```
+
+`add_new()` becomes much simpler:
+
+```{r}
+add_variable <- function(df, name, expr) {
+  mogrify(df, name ~ uq(expr))
+}
+add_variable(df, "z", ~ x + y)
+```
+
+### Exercises
+
+1.  Write a function that selects all rows of `df` where `variable` is 
+    greater than its mean. Make the function more general by allowing the
+    user to specify a function to use instead of `mean()` (e.g. `median()`).
+
+1.  Create a version of `mogrify()` where the first argument is `x`?
+    What happens if you try to create a new variable called `x`?
+
+## Non-standard evaluation
+
+In some situations you might want to eliminate the formula altogether, and allow the user to type expressions directly. I was once much enamoured with this approach (witness ggplot2, dplyr, ...). However, I now think that it should be used sparingly because explict quoting with `~` leads to simpler code, and makes it more clear to the user that something special is going on.
+
+That said, lazyeval does allow you to eliminate the `~` if you really want to. In this case, I recommend having both a NSE and SE version of the function. The SE version, which takes formuals, should have suffix `_`:
+
+```{r}
+sieve_ <- function(df, condition) {
+  rows <- f_eval(condition, df)
+  if (!is.logical(rows)) {
+    stop("`condition` must be logical.", call. = FALSE)
+  }
+  
+  rows[is.na(rows)] <- FALSE
+  df[rows, , drop = FALSE]
+}
+```
+
+Then create the NSE version which doesn't need the explicit formula. The key is the use of `f_capture()` which takes an unevaluated argument (a promise) and captures it as a formula:
+
+```{r}
+sieve <- function(df, expr) {
+  sieve_(df, f_capture(expr))
+}
+sieve(df, x == 1)
+```
+
+If you're familiar with `substitute()` you might expect the same drawbacks to apply. However, `f_capture()` is smart enough to follow a chain of promises back to the original value, so, for example, this code works fine:
+
+```{r}
+scramble <- function(df) {
+  df[sample(nrow(df)), , drop = FALSE]
+}
+subscramble <- function(df, expr) {
+  scramble(sieve(df, expr))
+}
+subscramble(df, x < 4)
+```
+
+### Dot-dot-dot
+
+If you want a `...` function that doesn't require formulas, I recommend that the SE version take a list of arguments, and the NSE version uses `dots_capture()` to capture multiple arguments as a list of formulas.
+
+```{r}
+mogrify_ <- function(`_df`, args) {
+  args <- as_f_list(args)
+  
+  for (nm in names(args)) {
+    `_df`[[nm]] <- f_eval(args[[nm]], `_df`)
+  }
+  
+  `_df`
+}
+
+mogrify <- function(`_df`, ...) {
+  mogrify_(`_df`, dots_capture(...))
+}
+```
+
+### Exercises
+
+1.  Recreate `subscramble()` using `base::subset()` instead of `sieve()`.
+    Why does it fail?
+
+## Metaprogramming
+
+The final use of non-standard evaluation is to do metaprogramming. This is a catch-all term that encompasses any function that does computation on an unevaluated expression. You can learn about metaprogrgramming in <http://adv-r.had.co.nz/Expressions.html>, particularly <http://adv-r.had.co.nz/Expressions.html#ast-funs>. Over time, the goal is to move all useful metaprogramming helper functions into this package, and discuss metaprogramming more here.
diff --git a/vignettes/lazyeval.nb.html b/vignettes/lazyeval.nb.html
new file mode 100644
index 0000000..64804e8
--- /dev/null
+++ b/vignettes/lazyeval.nb.html
@@ -0,0 +1,335 @@
+<!DOCTYPE html>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+
+<meta charset="utf-8">
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="pandoc" />
+
+
+<meta name="author" content="Hadley Wickham" />
+
+
+<title>Non-standard evaluation</title>
+
+<script src="data:application/x-javascript;base64,LyohIGpRdWVyeSB2MS4xMS4zIHwgKGMpIDIwMDUsIDIwMTUgalF1ZXJ5IEZvdW5kYXRpb24sIEluYy4gfCBqcXVlcnkub3JnL2xpY2Vuc2UgKi8KIWZ1bmN0aW9uKGEsYil7Im9iamVjdCI9PXR5cGVvZiBtb2R1bGUmJiJvYmplY3QiPT10eXBlb2YgbW9kdWxlLmV4cG9ydHM/bW9kdWxlLmV4cG9ydHM9YS5kb2N1bWVudD9iKGEsITApOmZ1bmN0aW9uKGEpe2lmKCFhLmRvY3VtZW50KXRocm93IG5ldyBFcnJvcigialF1ZXJ5IHJlcXVpcmVzIGEgd2luZG93IHdpdGggYSBkb2N1bWVudCIpO3JldHVybiBiKGEpfTpiKGEpfSgidW5kZWZpbmVkIiE9dHlwZW9mIHdpbmRvdz93aW5kb3c6dG [...]
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<link href="data:text/css;charset=utf-8,html%7Bfont%2Dfamily%3Asans%2Dserif%3B%2Dms%2Dtext%2Dsize%2Dadjust%3A100%25%3B%2Dwebkit%2Dtext%2Dsize%2Dadjust%3A100%25%7Dbody%7Bmargin%3A0%7Darticle%2Caside%2Cdetails%2Cfigcaption%2Cfigure%2Cfooter%2Cheader%2Chgroup%2Cmain%2Cmenu%2Cnav%2Csection%2Csummary%7Bdisplay%3Ablock%7Daudio%2Ccanvas%2Cprogress%2Cvideo%7Bdisplay%3Ainline%2Dblock%3Bvertical%2Dalign%3Abaseline%7Daudio%3Anot%28%5Bcontrols%5D%29%7Bdisplay%3Anone%3Bheight%3A0%7D%5Bhidden%5D%2Ctem [...]
+<script src="data:application/x-javascript;base64,LyohCiAqIEJvb3RzdHJhcCB2My4zLjUgKGh0dHA6Ly9nZXRib290c3RyYXAuY29tKQogKiBDb3B5cmlnaHQgMjAxMS0yMDE1IFR3aXR0ZXIsIEluYy4KICogTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlCiAqLwppZigidW5kZWZpbmVkIj09dHlwZW9mIGpRdWVyeSl0aHJvdyBuZXcgRXJyb3IoIkJvb3RzdHJhcCdzIEphdmFTY3JpcHQgcmVxdWlyZXMgalF1ZXJ5Iik7K2Z1bmN0aW9uKGEpeyJ1c2Ugc3RyaWN0Ijt2YXIgYj1hLmZuLmpxdWVyeS5zcGxpdCgiICIpWzBdLnNwbGl0KCIuIik7aWYoYlswXTwyJiZiWzFdPDl8fDE9PWJbMF0mJjk9PWJbMV0mJmJbMl08MSl0aHJvdy [...]
+<script src="data:application/x-javascript;base64,LyoqCiogQHByZXNlcnZlIEhUTUw1IFNoaXYgMy43LjIgfCBAYWZhcmthcyBAamRhbHRvbiBAam9uX25lYWwgQHJlbSB8IE1JVC9HUEwyIExpY2Vuc2VkCiovCi8vIE9ubHkgcnVuIHRoaXMgY29kZSBpbiBJRSA4CmlmICghIXdpbmRvdy5uYXZpZ2F0b3IudXNlckFnZW50Lm1hdGNoKCJNU0lFIDgiKSkgewohZnVuY3Rpb24oYSxiKXtmdW5jdGlvbiBjKGEsYil7dmFyIGM9YS5jcmVhdGVFbGVtZW50KCJwIiksZD1hLmdldEVsZW1lbnRzQnlUYWdOYW1lKCJoZWFkIilbMF18fGEuZG9jdW1lbnRFbGVtZW50O3JldHVybiBjLmlubmVySFRNTD0ieDxzdHlsZT4iK2IrIjwvc3R5bGU+IixkLm [...]
+<script src="data:application/x-javascript;base64,LyohIFJlc3BvbmQuanMgdjEuNC4yOiBtaW4vbWF4LXdpZHRoIG1lZGlhIHF1ZXJ5IHBvbHlmaWxsICogQ29weXJpZ2h0IDIwMTMgU2NvdHQgSmVobAogKiBMaWNlbnNlZCB1bmRlciBodHRwczovL2dpdGh1Yi5jb20vc2NvdHRqZWhsL1Jlc3BvbmQvYmxvYi9tYXN0ZXIvTElDRU5TRS1NSVQKICogICovCgovLyBPbmx5IHJ1biB0aGlzIGNvZGUgaW4gSUUgOAppZiAoISF3aW5kb3cubmF2aWdhdG9yLnVzZXJBZ2VudC5tYXRjaCgiTVNJRSA4IikpIHsKIWZ1bmN0aW9uKGEpeyJ1c2Ugc3RyaWN0IjthLm1hdGNoTWVkaWE9YS5tYXRjaE1lZGlhfHxmdW5jdGlvbihhKXt2YXIgYixjPWEuZG [...]
+
+<style type="text/css">code{white-space: pre;}</style>
+<link href="data:text/css;charset=utf-8,pre%20%2Eoperator%2C%0Apre%20%2Eparen%20%7B%0Acolor%3A%20rgb%28104%2C%20118%2C%20135%29%0A%7D%0Apre%20%2Eliteral%20%7B%0Acolor%3A%20rgb%2888%2C%2072%2C%20246%29%0A%7D%0Apre%20%2Enumber%20%7B%0Acolor%3A%20rgb%280%2C%200%2C%20205%29%3B%0A%7D%0Apre%20%2Ecomment%20%7B%0Acolor%3A%20rgb%2876%2C%20136%2C%20107%29%3B%0A%7D%0Apre%20%2Ekeyword%20%7B%0Acolor%3A%20rgb%280%2C%200%2C%20255%29%3B%0A%7D%0Apre%20%2Eidentifier%20%7B%0Acolor%3A%20rgb%280%2C%200%2C%20 [...]
+<script src="data:application/x-javascript;base64,CnZhciBobGpzPW5ldyBmdW5jdGlvbigpe2Z1bmN0aW9uIG0ocCl7cmV0dXJuIHAucmVwbGFjZSgvJi9nbSwiJmFtcDsiKS5yZXBsYWNlKC88L2dtLCImbHQ7Iil9ZnVuY3Rpb24gZihyLHEscCl7cmV0dXJuIFJlZ0V4cChxLCJtIisoci5jST8iaSI6IiIpKyhwPyJnIjoiIikpfWZ1bmN0aW9uIGIocil7Zm9yKHZhciBwPTA7cDxyLmNoaWxkTm9kZXMubGVuZ3RoO3ArKyl7dmFyIHE9ci5jaGlsZE5vZGVzW3BdO2lmKHEubm9kZU5hbWU9PSJDT0RFIil7cmV0dXJuIHF9aWYoIShxLm5vZGVUeXBlPT0zJiZxLm5vZGVWYWx1ZS5tYXRjaCgvXHMrLykpKXticmVha319fWZ1bmN0aW9uIGgodC [...]
+<style type="text/css">
+  pre:not([class]) {
+    background-color: white;
+  }
+</style>
+<script type="text/javascript">
+if (window.hljs && document.readyState && document.readyState === "complete") {
+   window.setTimeout(function() {
+      hljs.initHighlighting();
+   }, 0);
+}
+</script>
+
+
+
+<style type="text/css">
+h1 {
+  font-size: 34px;
+}
+h1.title {
+  font-size: 38px;
+}
+h2 {
+  font-size: 30px;
+}
+h3 {
+  font-size: 24px;
+}
+h4 {
+  font-size: 18px;
+}
+h5 {
+  font-size: 16px;
+}
+h6 {
+  font-size: 12px;
+}
+</style>
+
+
+</head>
+
+<body>
+
+<style type="text/css">
+.main-container {
+  max-width: 940px;
+  margin-left: auto;
+  margin-right: auto;
+}
+code {
+  color: inherit;
+  background-color: rgba(0, 0, 0, 0.04);
+}
+img {
+  max-width:100%;
+  height: auto;
+}
+.tabbed-pane {
+  padding-top: 12px;
+}
+button.code-folding-btn:focus {
+  outline: none;
+}
+</style>
+
+
+<div class="container-fluid main-container">
+
+<!-- tabsets -->
+<script src="data:application/x-javascript;base64,Cgp3aW5kb3cuYnVpbGRUYWJzZXRzID0gZnVuY3Rpb24odG9jSUQpIHsKCiAgLy8gYnVpbGQgYSB0YWJzZXQgZnJvbSBhIHNlY3Rpb24gZGl2IHdpdGggdGhlIC50YWJzZXQgY2xhc3MKICBmdW5jdGlvbiBidWlsZFRhYnNldCh0YWJzZXQpIHsKCiAgICAvLyBjaGVjayBmb3IgZmFkZSBhbmQgcGlsbHMgb3B0aW9ucwogICAgdmFyIGZhZGUgPSB0YWJzZXQuaGFzQ2xhc3MoInRhYnNldC1mYWRlIik7CiAgICB2YXIgcGlsbHMgPSB0YWJzZXQuaGFzQ2xhc3MoInRhYnNldC1waWxscyIpOwogICAgdmFyIG5hdkNsYXNzID0gcGlsbHMgPyAibmF2LXBpbGxzIiA6ICJuYXYtdGFicyI7CgogIC [...]
+<script>
+$(document).ready(function () {
+  window.buildTabsets("TOC");
+});
+</script>
+
+<!-- code folding -->
+<script src="data:application/x-javascript;base64,Cgp3aW5kb3cuaW5pdGlhbGl6ZUNvZGVGb2xkaW5nID0gZnVuY3Rpb24oc2hvdykgewoKICAvLyBoYW5kbGVycyBmb3Igc2hvdy1hbGwgYW5kIGhpZGUgYWxsCiAgJCgiI3JtZC1zaG93LWFsbC1jb2RlIikuY2xpY2soZnVuY3Rpb24oKSB7CiAgICAkKCdkaXYuci1jb2RlLWNvbGxhcHNlJykuZWFjaChmdW5jdGlvbigpIHsKICAgICAgJCh0aGlzKS5jb2xsYXBzZSgnc2hvdycpOwogICAgfSk7CiAgfSk7CiAgJCgiI3JtZC1oaWRlLWFsbC1jb2RlIikuY2xpY2soZnVuY3Rpb24oKSB7CiAgICAkKCdkaXYuci1jb2RlLWNvbGxhcHNlJykuZWFjaChmdW5jdGlvbigpIHsKICAgICAgJCh0aG [...]
+<script>
+$(document).ready(function () {
+  window.initializeCodeFolding("show" === "show");
+});
+</script>
+
+
+
+
+
+
+<div class="fluid-row" id="header">
+
+<div class="btn-group pull-right">
+<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><span>Code</span> <span class="caret"></span></button>
+<ul class="dropdown-menu" style="min-width: 50px;">
+<li><a id="rmd-show-all-code" href="#">Show All</a></li>
+<li><a id="rmd-hide-all-code" href="#">Hide All</a></li>
+</ul>
+</div>
+
+
+<h1 class="title toc-ignore">Non-standard evaluation</h1>
+<h4 class="author"><em>Hadley Wickham</em></h4>
+<h4 class="date"><em><code>r Sys.Date()</code></em></h4>
+
+</div>
+
+
+<p>This document describes lazyeval, a package that provides principled tools to perform non-standard evaluation (NSE) in R. You should read this vignette if you want to program with packages like dplyr and ggplot2<a href="#fn1" class="footnoteRef" id="fnref1"><sup>1</sup></a>, or you want a principled way of working with delayed expressions in your own package. As the name suggests, non-standard evaluation breaks away from the standard evaluation (SE) rules in order to do something spec [...]
+<ol style="list-style-type: decimal">
+<li><p><strong>Labelling</strong> enhances plots and tables by using the expressions supplied to a function, rather than their values. For example, note the axis labels in this plot:</p></li>
+<li><p><strong>Non-standard scoping</strong> looks for objects in places other than the current environment. For example, base R has <code>with()</code>, <code>subset()</code>, and <code>transform()</code> that look for objects in a data frame (or list) before the current environment:</p></li>
+<li><p><strong>Metaprogramming</strong> is a catch-all term that covers all other uses of NSE (such as in <code>bquote()</code> and <code>library()</code>). Metaprogramming is so called because it involves computing on the unevaluated code in some way.</p></li>
+</ol>
+<p>This document is broadly organised according to the three types of non-standard evaluation described above. The main difference is that after <a href="#labelling">labelling</a>, we’ll take a detour to learn more about <a href="#formulas">formulas</a>. You’re probably familiar with formulas from linear models (e.g. <code>lm(mpg ~ displ, data = mtcars)</code>) but formulas are more than just a tool for modelling: they are a general way of capturing an unevaluated expression.</p>
+<p>The approaches recommended here are quite different to my previous generation of recommendations. I am fairly confident these new approaches are correct, and will not have to change substantially again. The current tools make it easy to solve a number of practical problems that were previously challenging and are rooted in <a href="http://repository.readscheme.org/ftp/papers/pepm99/bawden.pdf">long-standing theory</a>.</p>
+<div id="labelling" class="section level2">
+<h2>Labelling</h2>
+<p>In base R, the classic way to turn an argument into a label is to use <code>deparse(substitute(x))</code>:</p>
+<p>There are two potential problems with this approach:</p>
+<ol style="list-style-type: decimal">
+<li><p>For long some expressions, <code>deparse()</code> generates a character vector with length > 1:</p></li>
+<li><p><code>substitute()</code> only looks one level up, so you lose the original label if the function isn’t called directly:</p></li>
+</ol>
+<p>Both of these problems are resolved by <code>lazyeval::expr_text()</code>:</p>
+<p>There are two variations on the theme of <code>expr_text()</code>:</p>
+<ul>
+<li><p><code>expr_find()</code> find the underlying expression. It works similarly to <code>substitute()</code> but will follow a chain of promises back up to the original expression. This is often useful for <a href="#metaprogramming">metaprogramming</a>.</p></li>
+<li><p><code>expr_label()</code> is a customised version of <code>expr_text()</code> that produces labels designed to be used in messages to the user:</p></li>
+</ul>
+<div id="exercises" class="section level3">
+<h3>Exercises</h3>
+<ol style="list-style-type: decimal">
+<li><p><code>plot()</code> uses <code>deparse(substitute(x))</code> to generate labels for the x and y axes. Can you generate input that causes it to display bad labels? Write your own wrapper around <code>plot()</code> that uses <code>expr_label()</code> to compute <code>xlim</code> and <code>ylim</code>.</p></li>
+<li><p>Create a simple implementation of <code>mean()</code> that stops with an informative error message if the argument is not numeric:</p></li>
+<li><p>Read the source code for <code>expr_text()</code>. How does it work? What additional arguments to <code>deparse()</code> does it use?</p></li>
+</ol>
+</div>
+</div>
+<div id="formulas" class="section level2">
+<h2>Formulas</h2>
+<p>Non-standard scoping is probably the most useful NSE tool, but before we can talk about a solid approach, we need to take a detour to talk about formulas. Formulas are a familiar tool from linear models, but their utility is not limited to models. In fact, formulas are a powerful, general purpose tool, because a formula captures two things:</p>
+<ol style="list-style-type: decimal">
+<li>An unevaluated expression.</li>
+<li>The context (environment) in which the expression was created.</li>
+</ol>
+<p><code>~</code> is a single character that allows you to say: “I want to capture the meaning of this code, without evaluating it right away”. For that reason, the formula can be thought of as a “quoting” operator.</p>
+<div id="definition-of-a-formula" class="section level3">
+<h3>Definition of a formula</h3>
+<p>Technically, a formula is a “language” object (i.e. an unevaluated expression) with a class of “formula” and an attribute that stores the environment:</p>
+<p>The structure of the underlying object is slightly different depending on whether you have a one-sided or two-sided formula:</p>
+<ul>
+<li><p>One-sided formulas have length two:</p></li>
+<li><p>Two-sided formulas have length three:</p></li>
+</ul>
+<p>To abstract away these differences, lazyeval provides <code>f_rhs()</code> and <code>f_lhs()</code> to access either side of the formula, and <code>f_env()</code> to access its environment:</p>
+</div>
+<div id="evaluating-a-formula" class="section level3">
+<h3>Evaluating a formula</h3>
+<p>A formula captures delays the evaluation of an expression so you can later evaluate it with <code>f_eval()</code>:</p>
+<p>This allows you to use a formula as a robust way of delaying evaluation, cleanly separating the creation of the formula from its evaluation. Because formulas capture the code and context, you get the correct result even when a formula is created and evaluated in different places. In the following example, note that the value of <code>x</code> inside <code>add_1000()</code> is used:</p>
+<p>It can be hard to see what’s going on when looking at a formula because important values are stored in the environment, which is largely opaque. You can use <code>f_unwrap()</code> to replace names with their corresponding values:</p>
+</div>
+<div id="non-standard-scoping" class="section level3">
+<h3>Non-standard scoping</h3>
+<p><code>f_eval()</code> has an optional second argument: a named list (or data frame) that overrides values found in the formula’s environment.</p>
+<p>This makes it very easy to implement non-standard scoping:</p>
+<p>One challenge with non-standard scoping is that we’ve introduced some ambiguity. For example, in the code below does <code>x</code> come from <code>mydata</code> or the environment?</p>
+<p>You can’t tell without knowing whether or not <code>mydata</code> has a variable called <code>x</code>. To overcome this problem, <code>f_eval()</code> provides two pronouns:</p>
+<ul>
+<li><code>.data</code> is bound to the data frame.</li>
+<li><code>.env</code> is bound to the formula environment.</li>
+</ul>
+<p>They both start with <code>.</code> to minimise the chances of clashing with existing variables.</p>
+<p>With these pronouns we can rewrite the previous formula to remove the ambiguity:</p>
+<p>If the variable or object doesn’t exist, you’ll get an informative error:</p>
+</div>
+<div id="unquoting" class="section level3">
+<h3>Unquoting</h3>
+<p><code>f_eval()</code> has one more useful trick up its sleeve: unquoting. Unquoting allows you to write functions where the user supplies part of the formula. For example, the following function allows you to compute the mean of any column (or any function of a column):</p>
+<p>To see how this works, we can use <code>f_interp()</code> which <code>f_eval()</code> calls internally (you shouldn’t call it in your own code, but it’s useful for debugging). The key is <code>uq()</code>: <code>uq()</code> evaluates its first (and only) argument and inserts the value into the formula:</p>
+<p>Unquoting allows you to create code “templates”, where you write most of the expression, while still allowing the user to control important components. You can even use <code>uq()</code> to change the function being called:</p>
+<p>Note that <code>uq()</code> only takes the RHS of a formula, which makes it difficult to insert literal formulas into a call:</p>
+<p>You can instead use <code>uqf()</code> which uses the whole formula, not just the RHS:</p>
+<p>Unquoting is powerful, but it only allows you to modify a single argument: it doesn’t allow you to add an arbitrary number of arguments. To do that, you’ll need “unquote-splice”, or <code>uqs()</code>. The first (and only) argument to <code>uqs()</code> should be a list of arguments to be spliced into the call:</p>
+</div>
+<div id="exercises-1" class="section level3">
+<h3>Exercises</h3>
+<ol style="list-style-type: decimal">
+<li><p>Create a wrapper around <code>lm()</code> that allows the user to supply the response and predictors as two separate formulas.</p></li>
+<li><p>Compare and contrast <code>f_eval()</code> with <code>with()</code>.</p></li>
+<li><p>Why does this code work even though <code>f</code> is defined in two places? (And one of them is not a function).</p></li>
+</ol>
+</div>
+</div>
+<div id="non-standard-scoping-1" class="section level2">
+<h2>Non-standard scoping</h2>
+<p>Non-standard scoping (NSS) is an important part of R because it makes it easy to write functions tailored for interactive data exploration. These functions require less typing, at the cost of some ambiguity and “magic”. This is a good trade-off for interactive data exploration because you want to get ideas out of your head and into the computer as quickly as possible. If a function does make a bad guess, you’ll spot it quickly because you’re working interactively.</p>
+<p>There are three challenges to implementing non-standard scoping:</p>
+<ol style="list-style-type: decimal">
+<li><p>You must correctly delay the evaluation of a function argument, capturing both the computation (the expression), and the context (the environment). I recommend making this explicit by requiring the user to “quote” any NSS arguments with <code>~</code>, and then evaluating explicit with <code>f_eval()</code>.</p></li>
+<li><p>When writing functions that use NSS-functions, you need some way to avoid the automatic lookup and be explicit about where objects should be found. <code>f_eval()</code> solves this problem with the <code>.data.</code> and <code>.env</code> pronouns.</p></li>
+<li><p>You need some way to allow the user to supply parts of a formula. <code>f_eval()</code> solves this with unquoting.</p></li>
+</ol>
+<p>To illustrate these challenges, I will implement a <code>sieve()</code> function that works similarly to <code>base::subset()</code> or <code>dplyr::filter()</code>. The goal of <code>sieve()</code> is to make it easy to select observations that match criteria defined by a logical expression. <code>sieve()</code> has three advantages over <code>[</code>:</p>
+<ol style="list-style-type: decimal">
+<li><p>It is much more compact when the condition uses many variables, because you don’t need to repeat the name of the data frame many times.</p></li>
+<li><p>It drops rows where the condition evaluates to <code>NA</code>, rather than filling them with <code>NA</code>s.</p></li>
+<li><p>It always returns a data frame.</p></li>
+</ol>
+<p>The implementation of <code>sieve()</code> is straightforward. First we use <code>f_eval()</code> to perform NSS. Then we then check that we have a logical vector, replace <code>NA</code>s with <code>FALSE</code>, and subset with <code>[</code>.</p>
+<div id="programming-with-sieve" class="section level3">
+<h3>Programming with <code>sieve()</code></h3>
+<p>Imagine that you’ve written some code that looks like this:</p>
+<p>(This is a contrived example, but it illustrates all of the important issues you’ll need to consider when writing more useful functions.)</p>
+<p>Instead of continuing to copy-and-paste your code, you decide to wrap up the common behaviour in a function:</p>
+<p>There are two ways that this function might fail:</p>
+<ol style="list-style-type: decimal">
+<li><p>The data frame might not have a variable called <code>x</code>. This will fail unless there’s a variable called <code>x</code> hanging around in the global environment:</p></li>
+<li><p>The data frame might have a variable called <code>threshold</code>:</p></li>
+</ol>
+<p>These failures are partiuclarly pernicious because instead of throwing an error they silently produce the wrong answer. Both failures arise because <code>f_eval()</code> introduces ambiguity by looking in two places for each name: the supplied data and formula environment.</p>
+<p>To make <code>threshold_x()</code> more reliable, we need to be more explicit by using the <code>.data</code> and <code>.env</code> pronouns:</p>
+<p>Here <code>.env</code> is bound to the environment where <code>~</code> is evaluated, namely the inside of <code>threshold_x()</code>.</p>
+</div>
+<div id="adding-arguments" class="section level3">
+<h3>Adding arguments</h3>
+<p>The <code>threshold_x()</code> function is not very useful because it’s bound to a specific variable. It would be more powerful if we could vary both the threshold and the variable it applies to. We can do that by taking an additional argument to specify which variable to use.</p>
+<p>One simple approach is to use a string and <code>[[</code>:</p>
+<p>This is a simple and robust solution, but only allows us to use an existing variable, not an arbitrary expression like <code>sqrt(x)</code>.</p>
+<p>A more general solution is to allow the user to supply a formula, and use unquoting:</p>
+<p>In this case, it’s the responsibility of the user to ensure the <code>variable</code> is specified unambiguously. <code>f_eval()</code> is designed so that <code>.data</code> and <code>.env</code> work even when evaluated inside of <code>uq()</code>:</p>
+</div>
+<div id="dot-dot-dot" class="section level3">
+<h3>Dot-dot-dot</h3>
+<p>There is one more tool that you might find useful for functions that take <code>...</code>. For example, the code below implements a function similar to <code>dplyr::mutate()</code> or <code>base::transform()</code>.</p>
+<p>(NB: the first argument is a non-syntactic name (i.e. it requires quoting with <code>`</code>) so it doesn’t accidentally match one of the names of the new variables.)</p>
+<p><code>transmogrifty()</code> makes it easy to add new variables to a data frame:</p>
+<p>One problem with this implementation is that it’s hard to specify the names of the generated variables. Imagine you want a function where the name and expression are in separate variables. This is awkward because the variable name is supplied as an argument name to <code>mogrify()</code>:</p>
+<p>Lazyeval provides the <code>f_list()</code> function to make writing this sort of function a little easier. It takes a list of formulas and evaluates the LHS of each formula (if present) to rename the elements:</p>
+<p>If we tweak <code>mogrify()</code> to use <code>f_list()</code> instead of <code>list()</code>:</p>
+<p><code>add_new()</code> becomes much simpler:</p>
+</div>
+<div id="exercises-2" class="section level3">
+<h3>Exercises</h3>
+<ol style="list-style-type: decimal">
+<li><p>Write a function that selects all rows of <code>df</code> where <code>variable</code> is greater than its mean. Make the function more general by allowing the user to specify a function to use instead of <code>mean()</code> (e.g. <code>median()</code>).</p></li>
+<li><p>Create a version of <code>mogrify()</code> where the first argument is <code>x</code>? What happens if you try to create a new variable called <code>x</code>?</p></li>
+</ol>
+</div>
+</div>
+<div id="non-standard-evaluation" class="section level2">
+<h2>Non-standard evaluation</h2>
+<p>In some situations you might want to eliminate the formula altogether, and allow the user to type expressions directly. I was once much enamoured with this approach (witness ggplot2, dplyr, …). However, I now think that it should be used sparingly because explict quoting with <code>~</code> leads to simpler code, and makes it more clear to the user that something special is going on.</p>
+<p>That said, lazyeval does allow you to eliminate the <code>~</code> if you really want to. In this case, I recommend having both a NSE and SE version of the function. The SE version, which takes formuals, should have suffix <code>_</code>:</p>
+<p>Then create the NSE version which doesn’t need the explicit formula. The key is the use of <code>f_capture()</code> which takes an unevaluated argument (a promise) and captures it as a formula:</p>
+<p>If you’re familiar with <code>substitute()</code> you might expect the same drawbacks to apply. However, <code>f_capture()</code> is smart enough to follow a chain of promises back to the original value, so, for example, this code works fine:</p>
+<div id="dot-dot-dot-1" class="section level3">
+<h3>Dot-dot-dot</h3>
+<p>If you want a <code>...</code> function that doesn’t require formulas, I recommend that the SE version take a list of arguments, and the NSE version uses <code>dots_capture()</code> to capture multiple arguments as a list of formulas.</p>
+</div>
+<div id="exercises-3" class="section level3">
+<h3>Exercises</h3>
+<ol style="list-style-type: decimal">
+<li>Recreate <code>subscramble()</code> using <code>base::subset()</code> instead of <code>sieve()</code>. Why does it fail?</li>
+</ol>
+</div>
+</div>
+<div id="metaprogramming" class="section level2">
+<h2>Metaprogramming</h2>
+<p>The final use of non-standard evaluation is to do metaprogramming. This is a catch-all term that encompasses any function that does computation on an unevaluated expression. You can learn about metaprogrgramming in <a href="http://adv-r.had.co.nz/Expressions.html" class="uri">http://adv-r.had.co.nz/Expressions.html</a>, particularly <a href="http://adv-r.had.co.nz/Expressions.html#ast-funs" class="uri">http://adv-r.had.co.nz/Expressions.html#ast-funs</a>. Over time, the goal is to mov [...]
+</div>
+<div class="footnotes">
+<hr />
+<ol>
+<li id="fn1"><p>Currently neither ggplot2 nor dplyr actually use these tools since I’ve only just figured it out. But I’ll be working hard to make sure all my packages are consistent in the near future.<a href="#fnref1">↩</a></p></li>
+</ol>
+</div>
+
+
+
+
+</div>
+
+<script>
+
+// add bootstrap table styles to pandoc tables
+$(document).ready(function () {
+  $('tr.header').parent('thead').parent('table').addClass('table table-condensed');
+});
+
+</script>
+
+<!-- dynamically load mathjax for compatibility with self-contained -->
+<script>
+  (function () {
+    var script = document.createElement("script");
+    script.type = "text/javascript";
+    script.src  = "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
+    document.getElementsByTagName("head")[0].appendChild(script);
+  })();
+</script>
+
+</body>
+<!-- rnb-cache-data-begin
+    chunks.json:eyJjaHVua19kZWZpbml0aW9ucyI6W10sImRvY193cml0ZV90aW1lIjoxNDYxMTY0NzI2fQ==
+rnb-cache-data-end -->
+<!-- rnb-document-source LS0tCnRpdGxlOiAiTm9uLXN0YW5kYXJkIGV2YWx1YXRpb24iCmF1dGhvcjogIkhhZGxleSBXaWNraGFtIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogcm1hcmtkb3duOjpodG1sX3ZpZ25ldHRlCnZpZ25ldHRlOiA+CiAgJVxWaWduZXR0ZUluZGV4RW50cnl7Tm9uLXN0YW5kYXJkIGV2YWx1YXRpb259CiAgJVxWaWduZXR0ZUVuZ2luZXtrbml0cjo6cm1hcmtkb3dufQogICVcVmlnbmV0dGVFbmNvZGluZ3tVVEYtOH0KLS0tCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpsaWJyYXJ5KGxhenlldmFsKQprbml0cjo6b3B0c19jaHVuayRzZXQoY29sbGFwc2UgPSBUUlVFLCBjb21tZW50ID0gIiM+IikKYGBgCgp [...]
+</html>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/r-cran-lazyeval.git



More information about the debian-med-commit mailing list