Skip to content
ry

Inline annotations

A #: comment repeats every parameter name of the function below it, so renaming a parameter means editing two places. Inline annotations put each type next to the name it belongs to, which is shorter to write and to read, and on ry’s runtime they are also checked when the code runs. The cost is that plain Rscript cannot run a file that has them, which is why this is a separate step. They are on by default; a project that must stay runnable by Rscript can turn them off.

You write a type right after a name, and a function’s return type after its parameters:

apply_discount <- function(price: double, rate: double = 0.2) -> double {
price * (1 - rate)
}
apply_discount("100")
type-mismatch
x expected `double`, found `character`

Every typed page of this guide already shows this spelling in its Inline tab, and it shares one type language with #: comments, so this page covers only what changes when you switch. Deleting each inline annotation turns a file into the plain R program it means. That includes @pure in front of function, the inline spelling of the #: @pure comment that strict mode reads.

Definitions are spelled type UserId = integer and alias Row = list{id: integer}, and @UserId(raw) creates a value of a nominal type, as custom types shows. A binding takes @trust, @if-unknown and @new between its : and its type, as in id: @new UserId <- 5L. type and alias are definitions only when a name follows them on the same line, so R code that uses them as ordinary names keeps working.

A statement may carry both spellings. If they give a parameter, the return, or the value different types, ry uses the inline type and warns:

#: fn(price: double, rate: double) -> double
apply_rate <- function(price: integer, rate) price * rate
annotation-mismatch
! the inline annotation gives parameter `price` the type `integer`, but the `#:` comment gives it `double`. ry uses the inline type; make the two agree, or delete one
--[R/both.R:2:31]
1 | #: fn(price: double, rate: double) -> double
2 | apply_rate <- function(price: integer, rate) price * rate
| ^^^^^^^
`-> > the `#:` comment's type is here
--[R/both.R:1:14]
1 | #: fn(price: double, rate: double) -> double
| ^^^^^^
2 | apply_rate <- function(price: integer, rate) price * rate

A #: definition directly above an inline definition of the same name is the same definition written twice, so it is not a duplicate. ry uses the inline one and warns when the two differ:

#: @type Celsius {double}
type Celsius = integer
annotation-mismatch
! the inline definition of `Celsius` says `integer`, but the `#:` comment says `double`. ry uses the inline definition; make the two agree, or delete one

ry run runs annotated code on ry’s own runtime, where the annotations also become checks: an argument of the wrong kind fails where the function was called. ry R runs it on GNU R as the plain program it means, with the annotations erased.

scale <- function(values: double, by: double) -> double values * by
scale("a", 1)
$ ry run scale.R
Error in scale("a", 1) :
argument `values` must be `double`, not `character`
Execution halted

A check runs when the argument is first used, so lazy arguments stay lazy, and it tests only the kind of value (double, character, a list, a function), not its length or class. Both commands also print the warnings above before the code runs. The type system reference has the full rules.

Turn inline annotations off when every file must also run under Rscript and other R tools, which cannot parse them:

[check]
inline-annotations = false

ry then reports every inline annotation it finds, so none slips into a file that must stay plain R. It warns once for the project and again at each annotation:

! inline type annotations are turned off (`[check] inline-annotations = false`), so ry does not check them and reports each one it finds
inline-annotation
! inline type annotations are turned off (`[check] inline-annotations = false`), so this one is not checked. R itself cannot run this code: delete the annotation, or turn inline annotations back on

ry run still runs such a file, but nothing checks the annotations. Use #: comments in such a project. The type system reference lists every place an inline annotation may go.