---
title: "Using {typed} in packages"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Using {typed} in packages}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

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

```{r setup}
library(typed, warn.conflicts = FALSE)
```

Here are some recommendations to use {typed} in your own package and define 
custom types..

## Use {typed} in your package

Call this to set up your package so you can use 'typed', by editing the DESCRIPTION file
and editing or creating 'R/your.pkg-package.R'

```{r, eval = FALSE}
typed::use_typed()
devtools::document() # to actually import from the generated roxygen2 comments
```

## Use existing types to define your functions

Here's an example where we define types for the return value, for all arguments, 
and for a `msg` variable in the body.

The use of roxygen2 tags is standard, except that you'll need to make sure to add the `@name` tag.

```{r}
#' add_or_subtract
#'
#' @param x double of length 1
#' @param y double of length 1
#' @param subtract whether to subtract instead of adding
#' @export
#' @name add_or_subtract
add_or_subtract <- Double(1) ? function (
    x = ? Double(1), 
    y = ? Double(1), 
    subtract = FALSE ? Logical(1, anyNA = FALSE)
  ) {
  Character(1) ? msg
  if(subtract) {
    msg <- "subtracting"
    message(msg)
    return(x - y)
  }
    msg <- "adding"
    message(msg)
  x + y
}
```

The created function will be the following:

```{r}
add_or_subtract
```

We see that `?` is not present in the
generated code, instead we have `check_arg()`, `declare()`, `check_output()`.
The substitution is done by the first `?`, it is both for efficiency and readability,
unfamiliar users might be intimidated by `?` and calls to `?` don't print nicely
in base R. 

Note that you're free to use these functions directly in your code if you don't
like the `?` Syntax.

## Define your own types from existing types

A way to define custom types is to wrap existing ones and add constraints

```{r}
Fruit <- function() {
  Character(
    length = 1, 
    ... = "`value` is not a fruit!" ~ . %in% c("apple", "pear", "cherry")
    # we could have several args named ... to apply multiple checks
  )
}

Fruit() ? x <- "potatoe"
```

The type `Any()` is the most general, but in many case it's a good idea to start
from a more restricted type so you get its own checks for free. 

```{r}
Fruit() ? x <- 1L
```

Here's a case where starting from `Any()` makes sense :

```{r}
Ggplot <- function() {
  Any(... = "Expected a ggplot object" ~ ggplot2::is.ggplot(value))
}

Ggplot() ? x <- 1
```


A custom type might also just be a restriction on an existing type using existing
arguments.

In the following example we apply a simple restriction to an existing type, using the existing
argument `length`.

Here we restrict the length but keep other args flexible by forwarding them:

```{r, error = TRUE}
ScalarInteger1 <- function(null_ok = FALSE, ...) {
  Integer(length = 1, null_ok = null_ok, ...)
}
ScalarInteger1() ? x <- c(1L, 2L)
```

Here we remove all flexibility:

```{r, error = TRUE}
ScalarInteger2 <- function() {
  Integer(length = 1)
}
ScalarInteger2() ? x <- c(1L, 2L)
```


## Define your own types from scratch

We can define a check function and use `as_assertion_factory()` on it.

```{r, error = TRUE}
check_is_ggplot <- function(x) {
  if(!ggplot2::is.ggplot(x)) {
    msg <- "Class mismatch"
    info1 <- "Expected a ggplot object"
    info2 <- sprintf("Got an object of class <%s>", paste(class(x), collapse = "/"))
   rlang::abort(
     c(msg, i = info1, x = info2)
   )
  }
}
Ggplot <- as_assertion_factory(check_is_ggplot)

Ggplot() ? x <- 1
```

Another example, to impose a data type based on a prototype:

```{r}
check_cars <- function(x) vctrs::vec_assert(x, cars)
Cars <- as_assertion_factory(check_cars)
Cars() ? x <- iris
```

Or if we want to be more general:

```{r}
check_valid_ptype <- function(x, ptype) vctrs::vec_assert(x, ptype)
Ptype <- as_assertion_factory(check_valid_ptype)
Ptype(cars) ? x <- iris
```

`as_assertion_factory()`
[is actually used to build the native assertion factories of this package](https://github.com/moodymudskipper/typed/blob/master/R/06_native_types.R)
