Language server
Works in any editor that supports LSP.
sales.R report.R
# one sale, and what is left of it after fees
sale: list{region: character, amount: double, closed: logical} <- list(region = "north", amount = 1240.5, closed = TRUE)
margin: <T: numeric, U: numeric> fn(gross: T, rate: U) -> double <- function(gross, rate) gross * (1 - rate)
net: double <- margin(sale$amount, 0.18)
label: character <- paste0(sale$region, ": ", round(net, 2))
message(label)
3 references
R/sales.R4:1margin <- function(gross,
rate) gross * (1 - rate)
R/sales.R6:8net <- margin(sale$amount, 0.18)
R/report.R2:28paste0(sale$region, " ", margin(sale$amount, rate))
Record fields, with their inferred types. Inferred type and definition site. In this file, another file, or a stub. Every read and write, across the workspace. Every occurrence of the binding. Inferred types. Nothing here is annotated.
Checker
Lints out of the box. Type checking with
[check] typing = true.
R/sales.R
read_sales <- function(path) {
read.csv(path, stringsAsFactors = FALSE)
}
top_regions <- function(sales, n) {
ranked = sales[order(-sales$amount), ]
head(unique(ranked$region), n)
}
report <- function(sales, regions) {
message(nrwo(sales), " rows in ", length(regions), " regions")
invisible(regions)
}
sales <- read_sales("sales.csv")
regions <- top_regions(sales, 5)
verbose <- T
if (verbose) report(sales, regions)
$ ry check
assignment-operator
! Use <-, not =, for assignment
--[R/sales.R:6:10]
5 | top_regions <- function(sales, n) {
6 | ranked = sales[order(-sales$amount), ]
| ^
7 | head(unique(ranked$region), n)
unresolved
! I could not resolve `nrwo` in this package, its imports, or builtins. Did you mean `nrow`?
--[R/sales.R:11:11]
10 | report <- function(sales, regions) {
11 | message(nrwo(sales), " rows in ", length(regions), " regions")
| ^^^^
12 | invisible(regions)
boolean-shorthand
! Use TRUE, not T, for Boolean values
--[R/sales.R:18:12]
17 |
18 | verbose <- T
| ^
19 | if (verbose) report(sales, regions)
3 problems in 1 file
Unresolved names and lints, with no setup.
exit 1
R/pricing.R
#: fn(gross: double, rate: double) -> double
net_of_fees <- function(gross, rate) {
gross * (1 - rate)
}
#: fn(region: character) -> double
fee_rate <- function(region) {
if (region == "north") 0.18 else 0.22
}
#: fn(rows: list{region: character, amount: double}) -> double
book_total <- function(rows) {
net_of_fees(rows$amount, fee_rate(rows$amount))
}
quote <- list(region = "north", amount = 1240.5)
total <- book_total(quote)
label <- paste0("total: ", total)
$ ry check
type-mismatch
x expected `character`, found `double`
--[R/pricing.R:13:37]
12 | book_total <- function(rows) {
13 | net_of_fees(rows$amount, fee_rate(rows$amount))
| ^^^^^^^^^^^
14 | }
1 problem in 1 file
Annotations are
#: comments.
exit 1
R/booking.R
net_of_fees: <T: numeric, U: numeric> fn(gross: T, rate: U) -> double <- function(gross, rate) {
gross * (1 - rate)
}
fee_rate: <T> fn(region: T) -> double <- function(region) {
if (region == "north") 0.18 else 0.22
}
book_total: <T, U: numeric> fn(rows: T, rate: U) -> double <- function(rows, rate) {
net_of_fees(rows$amount, rate)
}
quote: list{region: character, amount: double} <- list(region = "north", amount = 1240.5)
total <- book_total(quote, "0.18")
label: character <- paste0("total: ", total)
$ ry check
type-mismatch
x expected a numeric value (`integer` or `double`), found `character`
--[R/booking.R:14:28]
13 | quote <- list(region = "north", amount = 1240.5)
14 | total <- book_total(quote, "0.18")
| ^^^^^^
15 | label <- paste0("total: ", total)
1 problem in 1 file
No annotations. Inferred signatures shown in grey.
exit 1
Formatter
Normalizes spacing, indentation, and braces. Keeps your line breaks.
totals.R
as written
totals<-sapply(sales,function(s)s$amount)
top<-sales[totals>threshold ,]
message(paste0('kept: ',length(top),' of ',length(sales)))
ry fmt
totals <- sapply(sales, function(s) s$amount)
top <- sales[totals > threshold, ]
message(paste0("kept: ", length(top), " of ", length(sales)))
$ ry fmt
--diff totals.R Spaces around operators and after commas, double quotes. regions.R
as written
for(region in regions) accumulate(region)
if(nrow(top)==0)
··warning("no rows")
ry fmt
for (region in regions) {
··accumulate(region)
}
if (nrow(top) == 0) {
··warning("no rows")
}
$ ry fmt
--diff regions.R Loop bodies, and if bodies that span lines, always get braces. options.R
as written
opts <- list(
······host = "localhost",
·port = 8080,
········retries = 3
)
ry fmt
opts <- list(
··host = "localhost",
··port = 8080,
··retries = 3
)
$ ry fmt
--diff options.R One indent step per level. Line breaks are kept. pricing.R
as written
#:fn( gross:double,rate:double )->double
margin <- function(gross, rate) gross * (1 - rate)
#: list[ named : double ]
weights <- list(a = 1.0)
ry fmt
#: fn(gross: double, rate: double) -> double
margin <- function(gross, rate) gross * (1 - rate)
#: list[named: double]
weights <- list(a = 1.0)
$ ry fmt
--diff pricing.R Valid annotations are normalized. Others are left as written. Install
1 · Install
cargo install --git https://github.com/felix-andreas/ry ry-lang 2 · Run it on your project
ry fmt . # format
ry check . # lint and type-check No Rust toolchain? Download a prebuilt release, or install the VS Code extension, which bundles one.