---
title: "Unpacking Assignment"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Unpacking Assignment}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{R, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")

library(zeallot)
```

## Getting Started
The *zeallot* package defines an operator for *unpacking assignment*, sometimes
called *parallel assignment* or *destructuring assignment* in other programming
languages. The operator is written as `%<-%` and used like this.

```{r}
c(lat, lng) %<-% list(38.061944, -122.643889)
```

The result is that the list is unpacked into its elements,
and the elements are assigned to `lat` and `lng`.

```{r}
lat
lng
```

You can also unpack the elements of a vector.

```{r}
c(lat, lng) %<-% c(38.061944, -122.643889)
lat
lng
```

You can unpack much longer structures, too, of course, such as the 6-part
summary of a vector.

```{r}
c(min_wt, q1_wt, med_wt, mean_wt, q3_wt, max_wt) %<-% summary(mtcars$wt)
min_wt
q1_wt
med_wt
mean_wt
q3_wt
max_wt
```

If the left-hand side and right-hand sides do not match, an error is raised.
This guards against missing or unexpected values.

```{r, error=TRUE}
c(stg1, stg2, stg3) %<-% list("Moe", "Donald")
```

```{r, error=TRUE}
c(stg1, stg2, stg3) %<-% list("Moe", "Larry", "Curley", "Donald")
```

### Unpacking a returned value
A common use-case is when a function returns a list of values
and you want to extract the individual values.
In this example, the list of values returned by `coords_list()` is unpacked
into the variables `lat` and `lng`.

```{r}
#
# A function which returns a list of 2 numeric values.
# 
coords_list <- function() {
  list(38.061944, -122.643889)
}

c(lat, lng) %<-% coords_list()
lat
lng
```

In this next example, we call a function that returns a vector.

```{r}
#
# Convert cartesian coordinates to polar
#
to_polar = function(x, y) {
  c(sqrt(x^2 + y^2), atan(y / x))
}

c(radius, angle) %<-% to_polar(12, 5)
radius
angle
```

### Example: Intercept and slope of regression
You can directly unpack the coefficients of a simple linear regression
into the intercept and slope.

```{r}
c(inter, slope) %<-% coef(lm(mpg ~ cyl, data = mtcars))
inter
slope
```

### Example: Unpacking the result of `safely`
The *purrr* package includes the `safely` function.
It wraps a given function to create a new, "safe" version of the original function.

```{R, eval = require("purrr")}
safe_log <- purrr::safely(log)
```

The safe version returns a list of two items. The first item is the result of 
calling the original function, assuming no error occurred; or `NULL` if an error
did occur. The second item is the error, if an error occurred; or `NULL` if no 
error occurred. Whether or not the original function would have thrown an error,
the safe version will never throw an error.

```{r, eval = require("purrr")}
pair <- safe_log(10)
pair$result
pair$error
```

```{r, eval = require("purrr")}
pair <- safe_log("donald")
pair$result
pair$error
```

You can tighten and clarify calls to the safe function by using `%<-%`.

```{r, eval = require("purrr")}
c(res, err) %<-% safe_log(10)
res
err
```

## Unpacking a data frame
A data frame is simply a list of columns, so the *zeallot* assignment does
what you expect. It unpacks the data frame into individual columns.

```{r}
c(mpg=, cyl=, dist=, hp=) %<-% mtcars

cyl
```

### Example: List of data frames
Bear in mind, a list of data frames is still just a list. The assignment
will extract the list elements (which are data frames) but not unpack the data
frames themselves.

```{R}
c(gear3, gear4, gear5) %<-% split(mtcars, ~ gear)

head(gear3)

head(gear4)

gear5
```

The `%<-%` operator assigned four data frames to four variables, leaving the
data frames intact.

## Custom classes
*zeallot* includes implementations of `destructure` for data frames and linear
model summaries. However, because `destructure` is a generic function, you can
define new implementations for custom classes. When defining a new
implementation keep in mind the return value must be a list so the values are
properly unpacked.

## Trailing values: the "everything else" clause
In some cases, you want the first few elements of a list or vector but do not
care about the trailing elements. The `summary.lm` function, for example, 
returns a list of 11 values, and you may want only the first few. Fortunately,
there is a way to capture those first few and say "don't worry about everything
else".

```{r}
lm_mpg_cyl <- lm(mpg ~ cyl, data = mtcars)

c(lmc_call, lmc_terms, lmc_residuals, ..rest) %<-% summary(lm_mpg_cyl)

lmc_call

lmc_terms

head(lmc_residuals)
```

The collector variable `rest` captures everything else.

```{r}
str(rest)
```

Because `..rest` is prefixed with `..` a variable called `rest` is created
for the trailing values of the list.

## Leading values and middle values 

In addition to collecting trailing values, you can also collect initial values
and assign specific remaining values.

```{r}
c(..skip, e, f) %<-% list(1, 2, 3, 4, 5)
skip
e
f
```

Or you can assign the first value, skip values, and then assign the last value.

```{r}
c(begin, ..middle, end) %<-% list(1, 2, 3, 4, 5)
begin
middle
end
```

## Skipped values: anonymous elements

You can skip one or more values using a period (`.`) instead of a variable name.

```{r}
c(x, ., z) %<-% list(1, 2, 3)
x
z
```

You can skip many values with the anonymous collector (`..`).

```{r}
c(begin, .., end) %<-% list("hello", "blah", list("blah"), "blah", "world!")
begin
end
```

You can mix skips and collectors together to selectively keep and discard
elements.

```{r}
c(begin, ., ..middle, end) %<-% list(1, 2, 3, 4, 5)
begin
middle
end
```

## Default values: handle missing values
You can specify a default value for a left-hand side variable using `=`, similar
to specifying the default value of a function argument. This comes in handy when
the number of elements returned by a function cannot be guaranteed. `tail()` for
example may return fewer elements than asked for.

```{r}
nums <- c(1, 2)
c(x, y) %<-% tail(nums, 2)
x
y
```

However, if we tried to get the last 3 elements an error would be raised because
`tail(nums, 3)` still returns only 2 values.

```{r, error = TRUE}
c(x, y, z) %<-% tail(nums, 3)
```

We can fix the problem by specifying a default value for `z`.

```{r}
c(x, y, z = NULL) %<-% tail(nums, 3)
x
y
z
```

## Swapping values
A handy trick is swapping values without the use of a temporary variable.

```{r}
c(first, last) %<-% c("Ai", "Genly")
first
last

c(first, last) %<-% c(last, first)
first
last
```

or

```{r}
cat <- "meow"
dog <- "bark"

c(cat, dog, fish) %<-% c(dog, cat, dog)
cat
dog
fish
```
