updating API

This commit is contained in:
Tom Elliott 2025-01-07 09:58:01 +13:00
parent b2e62d757f
commit 7a4f649e70
6 changed files with 66 additions and 59 deletions

View File

@ -8,10 +8,10 @@ ts_compile.ts_function <- function(f, name = deparse(substitute(f)), ...) {
inputs <- attr(f, "args")
result <- attr(f, "result")
inputs <- sapply(inputs, \(x) x$type)
inputs <- sapply(inputs, \(x) x$zod_type)
fn_args <- paste(inputs) |>
paste(collapse = ", ")
sprintf("const %s = R.ocap([%s], %s]);", name, fn_args, result$type_fn)
sprintf("const %s = R.ocap([%s], %s]);", name, fn_args, result$r_type)
}
# #' @export

View File

@ -9,8 +9,8 @@
#' @param generic logical, if `TRUE` then the object is a generic type.
#'
#' @md
object <- function(type = "any",
type_fn = "any",
object <- function(zod_type = "any",
r_type = "any",
default = NULL,
check = function() stop("Not implemented"),
generic = FALSE) {
@ -27,21 +27,21 @@ object <- function(type = "any",
#' @export
print.ts_object <- function(x, ...) {
# name <- deparse(substitute(x))
cat(sprintf("Input (ts) type: %s\n", x$type))
cat(sprintf("Output (R) type: %s\n", x$type_fn))
cat(sprintf("Zod type: %s\n", x$zod_type))
cat(sprintf(" R type: %s\n", x$r_type))
}
is_object <- function(x) {
inherits(x, "ts_object")
}
ts_union <- function(...) paste(..., sep = " | ")
ts_array <- function(type = c("number", "boolean", "string")) {
if (type == "number") {
return("Float64Array")
ts_union <- function(...) sprintf("z.union([%s])", paste(..., sep = ", "))
ts_array <- function(type = c("z.number()", "z.boolean()", "z.string()")) {
if (type == "z.number()") {
return("z.instanceof(Float64Array)")
}
if (type == "boolean") {
return("Uint8Array")
if (type == "z.boolean()") {
return("z.instanceof(Uint8Array)")
}
return("RTYPE.stringArray")
}
@ -65,7 +65,7 @@ n_type_fun <- function(n, type) {
#' @export
ts_logical <- function(n = -1L) {
object(
n_type(n, "boolean"),
n_type(n, "z.boolean()"),
n_type_fun(n, "RTYPE.logical"),
check = function(x) {
if (!is.logical(x)) stop("Expected a boolean")
@ -78,7 +78,7 @@ ts_logical <- function(n = -1L) {
#' @export
ts_integer <- function(n = -1L) {
object(
n_type(n, "number"),
n_type(n, "z.number()"),
n_type_fun(n, "RTYPE.integer"),
check = function(x) {
if (!is.integer(x)) stop("Expected an integer")
@ -91,7 +91,7 @@ ts_integer <- function(n = -1L) {
#' @export
ts_numeric <- function(n = -1L) {
object(
n_type(n, "number"),
n_type(n, "z.number()"),
n_type_fun(n, "RTYPE.numeric"),
check = function(x) {
if (!is.numeric(x)) stop("Expected a number", call. = FALSE)
@ -106,7 +106,7 @@ ts_numeric <- function(n = -1L) {
#' @export
ts_character <- function(n = -1L) {
object(
n_type(n, "string"),
n_type(n, "z.string()"),
n_type_fun(n, "RTYPE.character"),
check = function(x) {
if (!is.character(x)) stop("Expected a string")

View File

@ -36,27 +36,24 @@ Writing functions is easy, just use the `ts_*()` functions to define formals and
```r
library(ts)
app <- ts_app(
app <- ts_list(
add = ts_fun(
function(x, y) {
x + y
},
x = ts_number(),
y = ts_number(),
x = ts_number(1),
y = ts_number(1),
# ideally this will use a generic type where x OR y can be vectors
# and, if one is a vector, the return type will be a vector too...
result = ts_number()
result = r_numeric(1)
),
sample = ts_fun(
function(x, n) {
sample(x, n)
},
x = ts_character_vector(),
n = ts_integer(),
result = ts_condition(n,
1 = ts_character(),
ts_character_vector()
)
x = ts_string(),
n = ts_number(1),
result = r_character()
)
)
@ -73,7 +70,7 @@ export const app = {
z.promise(R.numeric(1))
),
sample: z.function(
z.tuple([z.character_vector(), z.integer()]),
z.tuple([z.array(z.string()), z.integer()]),
z.promise(R.character())
)
};
@ -82,11 +79,8 @@ export const app = {
which will generate the following types:
```typescript
type App = {
add: (x: number, y: number) => Promise<{ data: number }>;
sample: (x: string[], n: number) => Promise<{ data: string | string[] }>;
// or, if possible, even better:
sample: <N extends number>(x: string[], n: N) =>
Promise<{ data: N extends 1 ? string : string[] }>;
add: (x: number, y: number) => Promise<Robj.Numeric<1>>;
sample: (x: string[], n: number) => Promise<Robj.Character>;
};
```

View File

@ -28,27 +28,24 @@ formals and return types.
``` r
library(ts)
app <- ts_app(
app <- ts_list(
add = ts_fun(
function(x, y) {
x + y
},
x = ts_number(),
y = ts_number(),
x = ts_number(1),
y = ts_number(1),
# ideally this will use a generic type where x OR y can be vectors
# and, if one is a vector, the return type will be a vector too...
result = ts_number()
result = r_numeric(1)
),
sample = ts_fun(
function(x, n) {
sample(x, n)
},
x = ts_character_vector(),
n = ts_integer(),
result = ts_condition(n,
1 = ts_character(),
ts_character_vector()
)
x = ts_string(),
n = ts_number(1),
result = r_character()
)
)
@ -66,7 +63,7 @@ export const app = {
z.promise(R.numeric(1))
),
sample: z.function(
z.tuple([z.character_vector(), z.integer()]),
z.tuple([z.array(z.string()), z.integer()]),
z.promise(R.character())
)
};
@ -76,11 +73,8 @@ which will generate the following types:
``` typescript
type App = {
add: (x: number, y: number) => Promise<{ data: number }>;
sample: (x: string[], n: number) => Promise<{ data: string | string[] }>;
// or, if possible, even better:
sample: <N extends number>(x: string[], n: N) =>
Promise<{ data: N extends 1 ? string : string[] }>;
add: (x: number, y: number) => Promise<Robj.Numeric<1>>;
sample: (x: string[], n: number) => Promise<Robj.Character>;
};
```
@ -113,7 +107,11 @@ cat(readLines("tests/testthat/app.R"), sep = "\n")
#> )
ts_compile("tests/testthat/app.R", file = "")
#> Error in ts_compile.ts_function(e[[x]], file = file, name = x): unused argument (file = file)
#> import { stringArray, character, numeric } from 'rserve-ts';
#>
#> const fn_first = R.ocap([z.union([z.string(), stringArray])], character(1)]);
#> const fn_mean = R.ocap([z.union([z.number(), z.instanceof(Float64Array)])], numeric(1)]);
#> const sample_num = R.ocap([z.instanceof(Float64Array)], numeric(1)]);
```
## TODO

View File

@ -5,23 +5,23 @@
\title{Typed object}
\usage{
object(
type = "any",
type_fn = "any",
zod_type = "any",
r_type = "any",
default = NULL,
check = function() stop("Not implemented"),
generic = FALSE
)
}
\arguments{
\item{type}{The type of the object that Typescript expect to send to R.}
\item{type_fn}{The type of the object that Typescript expects to recieve from R.}
\item{default}{The default value of the object.}
\item{check}{A function that checks the object and returns it if it is valid. This operates on the R side and is mostly for development and debugging purposes. It is up to the developer to ensure that all functions return the correct type of object always.}
\item{generic}{logical, if \code{TRUE} then the object is a generic type.}
\item{type}{The type of the object that Typescript expect to send to R.}
\item{type_fn}{The type of the object that Typescript expects to recieve from R.}
}
\description{
This is the base type for all typed objects. It is not meant to be used directly.

View File

@ -4,9 +4,24 @@ sample_num <- ts_function(
x = ts_numeric(0),
result = ts_numeric(1)
)
ts_compile(sample_num)
sampler <- ts_function(
function() {
list(
sample_one = sample_num(0)
)
},
result = ts_list(
num = sample_num
)
)
ts_compile(d_normal)
# compile to:
# const out = {
# sample_one: R.ocap([R.as_vector(z.number())], R.numeric(1)),
# };
# const sampler = R.ocap(
# [],
# R.list({
# num: R.ocap([], R.numeric(1))
# })
# );