R: Helpers for pairlist and language nodes

pairlist {rlang} | R Documentation

## Helpers for pairlist and language nodes

### Description

Like any parse tree, R expressions are structured as trees of nodes. Each node has two components: the head and the tail (though technically there is actually a third component for argument names, see details). Due to R's lisp roots, the head of a node (or cons cell) is called the CAR and the tail is called the CDR (pronounced car and cou-der). While R's ordinary subsetting operators have builtin support for indexing into these trees and replacing elements, it is sometimes useful to manipulate the nodes more directly. This is the purpose of functions like `node_car()` and `mut_node_car()`. They are particularly useful to prototype algorithms for your C-level functions.

- `node_car()` and `mut_node_car()` access or change the head of a node.

- `node_cdr()` and `mut_node_cdr()` access or change the tail of a node.

- Variants like `node_caar()` or `mut_node_cdar()` deal with the CAR of the CAR of a node or the CDR of the CAR of a node respectively. The letters in the middle indicate the type (CAR or CDR) and order of access.

- `node_tag()` and `mut_node_tag()` access or change the tag of a node. This is meant for argument names and should only contain symbols (not strings).

- `node()` creates a new node from two components.

### Usage

    node(newcar, newcdr)

    node_car(x)

    node_cdr(x)

    node_caar(x)

    node_cadr(x)

    node_cdar(x)

    node_cddr(x)

    mut_node_car(x, newcar)

    mut_node_cdr(x, newcdr)

    mut_node_caar(x, newcar)

    mut_node_cadr(x, newcar)

    mut_node_cdar(x, newcdr)

    mut_node_cddr(x, newcdr)

    node_tag(x)

    mut_node_tag(x, newtag)

### Arguments

`newcar, newcdr` | The new CAR or CDR for the node. These can be any R objects.
`x` | A language or pairlist node. Note that these functions are barebones and do not perform any type checking.
`newtag` | The new tag for the node. This should be a symbol.

### Details

R has two types of nodes to represent parse trees: language nodes, which represent function calls, and pairlist nodes, which represent arguments in a function call. These are the exact same data structures with a different name. This distinction is helpful for parsing the tree: the top-level node of a function call always has language type while its arguments have pairlist type.

Note that it is risky to manipulate calls at the node level. First, the calls are changed inplace. This is unlike base R operators which create a new copy of the language tree for each modification. To make sure modifying a language object does not produce side-effects, rlang exports the `duplicate()` function to create deep copy (or optionally a shallow copy, i.e. only the top-level node is copied). The second danger is that R expects language trees to be structured as a `NULL`-terminated list. The CAR of a node is a data slot and can contain anything, including another node (which is how you form trees, as opposed to mere linked lists). On the other hand, the CDR has to be either another node, or `NULL`. If it is terminated by anything other than the `NULL` object, many R commands will crash, including functions like `str()`. It is up to you to ensure that the language list you have modified is `NULL`-terminated.

Finally, all nodes can contain metadata in the TAG slot. This is meant for argument names and R expects tags to contain a symbol (not a string).

### Value

Setters like `mut_node_car()` invisibly return `x` modified in place. Getters return the requested node component.

### See Also

`duplicate()` for creating copy-safe objects, `lang_head()` and `lang_tail()` as slightly higher level alternatives that check their input, and `base::pairlist()` for an easier way of creating a linked list of nodes.

### Examples

    # Changing a node component happens in place and can have side
    # effects. Let's create a language object and a copy of it:
    lang <- quote(foo(bar))
    copy <- lang

    # Using R's builtin operators to change the language tree does not
    # create side effects:
    copy[[2]] <- quote(baz)
    copy
    lang

    # On the other hand, the CAR and CDR operators operate in-place. Let's
    # create new objects since the previous examples triggered a copy:
    lang <- quote(foo(bar))
    copy <- lang

    # Now we change the argument pairlist of `copy`, making sure the new
    # arguments are NULL-terminated:
    mut_node_cdr(copy, node(quote(BAZ), NULL))

    # Or equivalently:
    mut_node_cdr(copy, pairlist(quote(BAZ)))
    copy

    # The original object has been changed in place:
    lang

[Package rlang version 0.1.6 Index]