---
title: "Consistency checking with pkgutils"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Consistency checking with pkgutils}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

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

`pkgutils` is primarily designed to support R package developers by providing
functions for handling missing data, consistency checks, and parametrising
per group analyses. 

One area where a lot of boiler plate can be avoided is
when a function can take multiple combinations of optional inputs that resolve
to each other, and where one or more variables can be recycled. 

This can require complex logic to handle missing values when some
but not all of the parameters are provided and can have inconsistencies if a user 
provides all of the components. Similarly providing lists of different lengths 
to such functions can cause issues.

# Toy example

Suppose we are creating a function that does some mechanics

```{r}

newton2 = function(f, m = 5, a) {
  # ensure parameters provided are the same length
  pkgutils::recycle(f,m,a)
  # ensure parameters f,m and a are numeric and coerce them if not.
  pkgutils::check_numeric(f,m,a)
  # fill in missing variables using the relationships given.
  pkgutils::resolve_missing(
    f = m*a,
    a = f/m,
    m = f/a
  )
  # do something useful here...
  return(list(f=f,m=m,a=a))
}
```

Inferring the acceleration, including coercion of `f`:

```{r}
newton2(f="10",m=2)
```

Inferring the acceleration, when recycling the mass parameter:

```{r}
newton2(f=1:10, m=2)
```

A default value for the mass is given in the function. Inferring it therefore
needs the user to provide an explicit NULL value otherwise the default prevents
inference and the constraints fail as follows:

```{r}
try(newton2(f=1:10, a=0.5))
```

Combining inferring values with specified default values leads to a slightly 
counter-intuitive behaviour so this pattern is not recommended. It is better not 
to specify both a default value and constraints for mass.

```{r}
newton2(f=1:10, m=NULL, a=0.5)
```

Inferring the force:

```{r}
newton2(m=2,a=1:10)
```

If insufficient information is provided to calculate the remaining variables
an error is thrown:

```{r}

try(newton2(m=2))
```

If a default value is given then that is used in the inference if it is not
specified and recycled as needed, but I still think this has the potential to 
be confusing:

```{r}
newton2(f=1:10)
```

