Run on ry's runtime
Goal: run your R code on ry’s own runtime, and use the features GNU R cannot parse: inline type
annotations that are also checked at run time, and records, tagged unions and match.
Run code
Section titled “Run code”| Command | Runs |
|---|---|
ry run script.R |
a script on ry’s runtime; arguments after it reach commandArgs(TRUE) |
ry run |
a console on ry’s runtime |
ry R script.R, ry R |
the same on GNU R, with inline annotations erased |
ry test |
the package’s testthat tests; --filter NAME, --watch |
ry document |
roxygen2, writing the same man/*.Rd and NAMESPACE byte for byte |
ry load -e EXPR |
loads the package, evaluates EXPR, exits |
Inside a package, ry run opens a console that loads the package, then reloads exactly the
definitions you changed before each command. There is no load_all() to call and no session to
restart. Take a package tally whose R/count.R has a bug:
count_words <- function(text) length(strsplit(text, " ")[[1]])$ ry runLoaded tally: 1 definition bound lazily, 0 top-level statements run, in 6 ms.ry console on ry's runtime. Type q() or Ctrl-D to quit.> count_words("the quick fox")[1] 4The double space makes an empty word. Change " " to "[[:space:]]+", save, and run the same
command:
> count_words("the quick fox")Reloaded tally: 1 definition rebound, in 13 ms.[1] 3In RStudio or another R session, source(pipe("ry load --r-entry")) defines a load_all() that
loads the same way; put the line in ~/.Rprofile. ry’s runtime is incomplete: code that needs
something it lacks stops with an error that names it, and ry R runs the same code on GNU R.
Inline annotations
Section titled “Inline annotations”An inline annotation, the Inline tab of step 2, means the same to
the checker as its #: comment. On ry’s runtime it is also checked when the code runs:
scale <- function(values: double, by: double) -> double values * byscale("a", 1)$ ry run scale.RError in scale("a", 1) : argument `values` must be `double`, not `character`The check tests the kind of value (double, character, a list, a function), not its length or
class, and runs when the argument is first used, so lazy arguments stay lazy. ry R erases the
annotations, so GNU R runs the file as plain R. A project whose files must also run under Rscript
sets inline-annotations = false under [check], and ry then reports every inline annotation.
When a statement has both spellings and they disagree, ry uses the inline one and warns with
annotation-mismatch.
Records, tags and match
Section titled “Records, tags and match”ry has its own data types, which a checker can follow completely where R’s lists, S4 and R6 classes defeat it. They run only on ry’s runtime:
point <- (x = 1, y = 2) # Record{x: double, y: double}pair <- (1L, "a") # Tuple{integer, character}one <- (x = 1,) # a one-field record needs the trailing commapoint$x + point$y[1] 3A record’s fields are fixed and checked, and it has value semantics: point$x <- 3 changes a copy
only point sees. A tag is a name starting with :, with an optional payload, and a union of
tags lists every case a value can be. match runs the first arm whose pattern fits, and ry reports
a case no arm handles:
alias Shape = | :Circle(radius: double) | :Rect(width: double, height: double) | :Empty
area <- function(s: Shape) -> double { match (s) { :Circle(radius = r) => pi * r^2 :Rect(width = w, height = h) => w * h }}#: @alias Shape {#: | :Circle(radius: double)#: | :Rect(width: double, height: double)#: | :Empty#: }
#: fn(s: Shape) -> doublearea <- function(s) { match (s) { :Circle(radius = r) => pi * r^2 :Rect(width = w, height = h) => w * h }}non-exhaustive-match
x this `match` does not handle `:Empty`: add an arm for it, or a catch-all `_ => ...` armAdd :Empty => 0 and area(:Rect(width = 2, height = 3)) returns 6. Patterns are _, a name, a
literal, a tag with payload patterns, (p, q), (x = p, ..), p | q, and a guard
p if (cond).
R functions do not know these values, so passing one to an R function is an error until you convert
it with as.list():
point <- (x = 1, y = 2)sum(point)type-mismatch
x `sum` is an R function, and this value is a ry record, not an R object. Convert it with `as.list()` first[1, 2, 3] is an Array, ry’s strict vector: one item type, no NA, no names and no recycling.
Type system has its rules.
Next: go fully typed.