*{typed}* implements a type system for R, it has 3 main
features:

- set variable types in a script or the body of a function, so they can’t be assigned illegal values
- set argument types in a function definition
- set return type of a function

The user can define their own types, or leverage assertions from other packages.

Under the hood variable types use active bindings, so once a variable is restricted by an assertion, it cannot be modified in a way that would not satisfy it.

Install with:

`::install_github("moodymudskipper/typed") remotes`

And attach with :

```
# masking warning about overriding `?`
library(typed, warn.conflicts = FALSE)
```

`declare`

Here are examples on how we would set types

```
Character() ? x # restrict x to "character" type
<- "a"
x
x#> [1] "a"
Integer(3) ? y <- 1:3 # restrict y to "integer" type of length 3
y#> [1] 1 2 3
```

We cannot assign values of the wrong type to `x`

and
`y`

anymore.

```
<- 2
x #> Error: type mismatch
#> `typeof(value)`: "double"
#> `expected`: "character"
<- 4:5
y #> Error: length mismatch
#> `length(value)`: 2
#> `expected`: 3
1] <- 10
y[#> Error: type mismatch
#> `typeof(value)`: "character"
#> `expected`: "integer"
```

But the right type will work.

```
<- c("b", "c")
x
<- c(1L, 10L, 100L) y
```

`declare`

is a strict equivalent, slightly more efficient,
which looks like `base::assign`

.

```
declare("x", Character())
<- "a"
x
x#> [1] "a"
declare("y", Integer(3), 1:3)
y#> [1] 1 2 3
```

`Integer`

and `Character`

are function
factories (functions that return functions), thus
`Integer(3)`

and `Character()`

are functions.

The latter functions operate checks on a value and in case of success return this value, generally unmodified. For instance :

```
Integer(3)(1:2)
#> Error: length mismatch
#> `length(value)`: 2
#> `expected`: 3
Character()(3)
#> Error: type mismatch
#> `typeof(value)`: "double"
#> `expected`: "character"
```

We call `Integer(3)`

and `Character()`

assertions, and we call `Integer`

and `Character`

assertion factories.

The package contains many assertion factories (see
`?assertion_factories`

), the main ones are:

`Any`

(No default restriction)`Logical`

`Integer`

`Double`

`Character`

`List`

`Environment`

`Factor`

`Matrix`

`Data.frame`

`Date`

`Time`

(POSIXct)

As we’ve seen with `Integer(3)`

, passing arguments to a
assertion factory restricts the type.

For instance `Integer`

has arguments `length`

`null_ok`

and `...`

, we already used
`length`

, `null_ok`

is convenient to allow a
default `NULL`

value in addition to the
`"integer"`

type. In the dots we can use arguments named as
functions and with the value of the expected result.

```
Integer(anyNA = FALSE) ? x <- c(1L, 2L, NA)
#> Error: `anyNA` mismatch
#> `anyNA(value)`: TRUE
#> `expected`: FALSE
```

Useful arguments might be for instance,
`anyDuplicated = 0L`

, `names = NULL`

,
`attributes = NULL`

… Any available function can be used.

That makes assertion factories very flexible! If it is still not
flexible enough, one can provide conditions using formulas in the
`...`

. Be careful to skip all named arguments by adding
comas, or name the formula arguments `...`

.

```
<- Character(1, ... = "`value` is not a fruit!" ~ . %in% c("apple", "pear", "cherry"))
fruit
<- "potatoe"
fruit ? x #> Error: `value` is not a fruit!
#> `value %in% c("apple", "pear", "cherry")`: FALSE
#> `expected`: TRUE
```

The arguments can differ between assertion factories, for instance
`Data.frame`

has `nrow`

, `ncol`

,
`each`

, `null_ok`

and `...`

```
Data.frame() ? x <- iris
Data.frame(ncol = 2) ? x <- iris
#> Error: Column number mismatch
#> `ncol(value)`: 5
#> `expected`: 2
Data.frame(each = Double()) ? x <- iris
#> Error: column 5 ("Species") type mismatch
#> `typeof(value)`: "integer"
#> `expected`: "double"
```

Some great packages provide assertions, and they can be used with
`typed`

provided that they take the object as a first input
and return the object if no failure. Richie Cotton’s
*{assertive}* and Michel Lang’s *{checkmate}* both
qualify.

```
library(assertive)
assert_is_monotonic_increasing ? z<- 3:1
z #> Error: is_monotonic_increasing : The values of assigned_value are not monotonic increasing.
#> Position ValueBefore ValueAfter
#> 1 1/2 3 2
#> 2 2/3 2 1
```

If we want to use more than the first argument, we should create an assertion factory :

```
<- as_assertion_factory(assert_is_monotonic_increasing)
Monotonic_incr Monotonic_incr(strictly = TRUE) ? z
<- c(1, 1, 2)
z #> Error: is_monotonic_increasing : The values of value are not strictly monotonic increasing.
#> Position ValueBefore ValueAfter
#> 1 1/2 1 1
```

`as_assertion_factory`

can be used to create your own
assertion factories from scratch too, in fact it’s
used to build the native assertion factories of this package .

To define a constant, we just surround the variable by parentheses (think of them as a protection)

```
Double() ? (x) <- 1
<- 2
x #> Error: Can't assign to a constant
<- 1
? (y) <- 2
y #> Error: Can't assign to a constant
```

We can set argument types this way :

```
<- ? function (x= ? Double(), y= 1 ? Double()) {
add + y
x }
```

Note that we started the definition with a `?`

, and that
we gave a default to `y`

, but not `x`

. Note also
the `=`

sign next to `x`

, necessary even when we
have no default value. If you forget it you’ll have an error “unexpected
`?`

in …”.

This created the following function, by adding checks at the top of the body

```
add#> # typed function
#> function (x, y = 1)
#> {
#> check_arg(x, Double())
#> check_arg(y, Double())
#> x + y
#> }
#> # Arg types:
#> # x: Double()
#> # y: Double()
```

Let’s test it by providing a right and wrong type.

```
add(2, 3)
#> [1] 5
add(2, 3L)
#> Error: In `add(2, 3L)` at `check_arg(y, Double())`:
#> wrong argument to function, type mismatch
#> `typeof(value)`: "integer"
#> `expected`: "double"
```

If we want to restrict `x`

and `y`

to the type
“integer” in the rest of the body of the function we can use the
`?+`

notation :

```
<- ? function (x= ?+ Double(), y= 1 ?+ Double()) {
add + y
x
}
add#> # typed function
#> function (x, y = 1)
#> {
#> check_arg(x, Double(), .bind = TRUE)
#> check_arg(y, Double(), .bind = TRUE)
#> x + y
#> }
#> # Arg types:
#> # x: Double()
#> # y: Double()
```

We see that it is translated into a `check_arg`

call
containing a `.bind = TRUE`

argument.

I we want to restrict the quoted expression rather than the value of
an argument, we can use `?~`

:

```
<- ? function (x= ?~ Symbol()) {
identity_sym_only
x
}
<- 1
a identity_sym_only(a)
#> [1] 1
identity_sym_only(a + a)
#> Error: In `identity_sym_only(a + a)` at `check_arg(substitute(x), Symbol())`:
#> wrong argument to function, type mismatch
#> `typeof(value)`: "language"
#> `expected`: "symbol"
identity_sym_only#> # typed function
#> function (x)
#> {
#> check_arg(substitute(x), Symbol())
#> x
#> }
#> <bytecode: 0x000000001cfe6008>
#> # Arg types:
#> # x: ~Symbol()
```

We see that it is translated into a `check_arg`

call
containing a call to `substitute`

as the first argument. The
`~`

is kept in the attributes of the function.

We can also check the `...`

, for instance use
`function(... = ? Integer())`

to check that only integers are
passed to the dots, and use `function(... = ?~ Symbol())`

to
check that all quoted values passed to `...`

are symbols.

The special assertion factory `Dots`

can also be used, in
that case the checks will apply to `list(...)`

rather than to
each element individually, for instance
`function(... = ? Dots(2))`

makes sure the dots were fed 2
values. In a similar fashion `function(... = ?~ Dots(2))`

can
be used to apply checks to the list of quoted argument passed to
`...`

.

To set a return type we use `?`

before the function
definition as in the previous section, but we type an assertion on the
left hand side.

```
<- Double() ? function (x, y, subtract = FALSE) {
add_or_subtract if(subtract) return(x - y)
+ y
x
}
add_or_subtract#> # typed function
#> function (x, y, subtract = FALSE)
#> {
#> if (subtract)
#> return(check_output(x - y, Double()))
#> check_output(x + y, Double())
#> }
#> # Return type: Double()
```

We see that the returned values have been wrapped inside
`check_output`

calls.

Let’s define our function for our package and document it with
*{roxygen2}*. It is documented as usual,except that you’ll need
to make sure to add the `@name`

tag.

We declare types for the return value, for all arguments, and we
declare a string `msg`

.

```
#' 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) {
<- "subtracting"
msg message(msg)
return(x - y)
}<- "adding"
msg message(msg)
+ y
x }
```

The created function will be the following, we see that
`Character(1) ? msg`

was changed into a `declare`

call too, this is both for efficiency and readability. Unfamiliar users
might be intimidated by `?`

and calls to `?`

don’t
print nicely.

```
add_or_subtract#> # typed function
#> function (x, y, subtract = FALSE)
#> {
#> check_arg(x, Double(1))
#> check_arg(y, Double(1))
#> check_arg(subtract, Logical(1, anyNA = FALSE))
#> declare("msg", Character(1))
#> if (subtract) {
#> msg <- "subtracting"
#> message(msg)
#> return(check_output(x - y, Double(1)))
#> }
#> msg <- "adding"
#> message(msg)
#> check_output(x + y, Double(1))
#> }
#> # Return type: Double(1)
#> # Arg types:
#> # x: Double(1)
#> # y: Double(1)
#> # subtract: Logical(1, anyNA = FALSE)
```

Note that your package would import *{typed}* but
`?`

won’t be exposed to the user, they will see it in the
code but will be able to use `?`

just as before. In fact the
most common standard use `?mean`

still works even when
*{typed}* is attached.

This is inspired in good part by Jim Hester and Gabor Csardi’s work and many great efforts on static typing, assertions, or annotations in R, in particular:

- Gabor Csardy’s
*{argufy}* - Richie Cotton’s
*{assertive}* - Tony Fishettti’s
*{assertr*} - Hadley Wickham’s
*{assertthat}* - Michel Lang’s
*{checkmate}* - Joe Thorley’s
*{checkr}* - Joe Thorley’s
*{chk}* - Aviral Goel’s
*{contractr}* - Stefan Bache’s
*{ensurer}* - Brian Lee Yung Rowe’s
*{lambda.r}* - Kun Ren’s
*{rtype}* - Jim Hester’s
*{types}*