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") inputs <- attr(f, "args")
result <- attr(f, "result") result <- attr(f, "result")
inputs <- sapply(inputs, \(x) x$type) inputs <- sapply(inputs, \(x) x$zod_type)
fn_args <- paste(inputs) |> fn_args <- paste(inputs) |>
paste(collapse = ", ") 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 # #' @export

View File

@ -9,8 +9,8 @@
#' @param generic logical, if `TRUE` then the object is a generic type. #' @param generic logical, if `TRUE` then the object is a generic type.
#' #'
#' @md #' @md
object <- function(type = "any", object <- function(zod_type = "any",
type_fn = "any", r_type = "any",
default = NULL, default = NULL,
check = function() stop("Not implemented"), check = function() stop("Not implemented"),
generic = FALSE) { generic = FALSE) {
@ -27,21 +27,21 @@ object <- function(type = "any",
#' @export #' @export
print.ts_object <- function(x, ...) { print.ts_object <- function(x, ...) {
# name <- deparse(substitute(x)) # name <- deparse(substitute(x))
cat(sprintf("Input (ts) type: %s\n", x$type)) cat(sprintf("Zod type: %s\n", x$zod_type))
cat(sprintf("Output (R) type: %s\n", x$type_fn)) cat(sprintf(" R type: %s\n", x$r_type))
} }
is_object <- function(x) { is_object <- function(x) {
inherits(x, "ts_object") inherits(x, "ts_object")
} }
ts_union <- function(...) paste(..., sep = " | ") ts_union <- function(...) sprintf("z.union([%s])", paste(..., sep = ", "))
ts_array <- function(type = c("number", "boolean", "string")) { ts_array <- function(type = c("z.number()", "z.boolean()", "z.string()")) {
if (type == "number") { if (type == "z.number()") {
return("Float64Array") return("z.instanceof(Float64Array)")
} }
if (type == "boolean") { if (type == "z.boolean()") {
return("Uint8Array") return("z.instanceof(Uint8Array)")
} }
return("RTYPE.stringArray") return("RTYPE.stringArray")
} }
@ -65,7 +65,7 @@ n_type_fun <- function(n, type) {
#' @export #' @export
ts_logical <- function(n = -1L) { ts_logical <- function(n = -1L) {
object( object(
n_type(n, "boolean"), n_type(n, "z.boolean()"),
n_type_fun(n, "RTYPE.logical"), n_type_fun(n, "RTYPE.logical"),
check = function(x) { check = function(x) {
if (!is.logical(x)) stop("Expected a boolean") if (!is.logical(x)) stop("Expected a boolean")
@ -78,7 +78,7 @@ ts_logical <- function(n = -1L) {
#' @export #' @export
ts_integer <- function(n = -1L) { ts_integer <- function(n = -1L) {
object( object(
n_type(n, "number"), n_type(n, "z.number()"),
n_type_fun(n, "RTYPE.integer"), n_type_fun(n, "RTYPE.integer"),
check = function(x) { check = function(x) {
if (!is.integer(x)) stop("Expected an integer") if (!is.integer(x)) stop("Expected an integer")
@ -91,7 +91,7 @@ ts_integer <- function(n = -1L) {
#' @export #' @export
ts_numeric <- function(n = -1L) { ts_numeric <- function(n = -1L) {
object( object(
n_type(n, "number"), n_type(n, "z.number()"),
n_type_fun(n, "RTYPE.numeric"), n_type_fun(n, "RTYPE.numeric"),
check = function(x) { check = function(x) {
if (!is.numeric(x)) stop("Expected a number", call. = FALSE) if (!is.numeric(x)) stop("Expected a number", call. = FALSE)
@ -106,7 +106,7 @@ ts_numeric <- function(n = -1L) {
#' @export #' @export
ts_character <- function(n = -1L) { ts_character <- function(n = -1L) {
object( object(
n_type(n, "string"), n_type(n, "z.string()"),
n_type_fun(n, "RTYPE.character"), n_type_fun(n, "RTYPE.character"),
check = function(x) { check = function(x) {
if (!is.character(x)) stop("Expected a string") 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 ```r
library(ts) library(ts)
app <- ts_app( app <- ts_list(
add = ts_fun( add = ts_fun(
function(x, y) { function(x, y) {
x + y x + y
}, },
x = ts_number(), x = ts_number(1),
y = ts_number(), y = ts_number(1),
# ideally this will use a generic type where x OR y can be vectors # 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... # and, if one is a vector, the return type will be a vector too...
result = ts_number() result = r_numeric(1)
), ),
sample = ts_fun( sample = ts_fun(
function(x, n) { function(x, n) {
sample(x, n) sample(x, n)
}, },
x = ts_character_vector(), x = ts_string(),
n = ts_integer(), n = ts_number(1),
result = ts_condition(n, result = r_character()
1 = ts_character(),
ts_character_vector()
)
) )
) )
@ -73,7 +70,7 @@ export const app = {
z.promise(R.numeric(1)) z.promise(R.numeric(1))
), ),
sample: z.function( sample: z.function(
z.tuple([z.character_vector(), z.integer()]), z.tuple([z.array(z.string()), z.integer()]),
z.promise(R.character()) z.promise(R.character())
) )
}; };
@ -82,11 +79,8 @@ export const app = {
which will generate the following types: which will generate the following types:
```typescript ```typescript
type App = { type App = {
add: (x: number, y: number) => Promise<{ data: number }>; add: (x: number, y: number) => Promise<Robj.Numeric<1>>;
sample: (x: string[], n: number) => Promise<{ data: string | string[] }>; sample: (x: string[], n: number) => Promise<Robj.Character>;
// or, if possible, even better:
sample: <N extends number>(x: string[], n: N) =>
Promise<{ data: N extends 1 ? string : string[] }>;
}; };
``` ```

View File

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

View File

@ -5,23 +5,23 @@
\title{Typed object} \title{Typed object}
\usage{ \usage{
object( object(
type = "any", zod_type = "any",
type_fn = "any", r_type = "any",
default = NULL, default = NULL,
check = function() stop("Not implemented"), check = function() stop("Not implemented"),
generic = FALSE generic = FALSE
) )
} }
\arguments{ \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{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{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{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{ \description{
This is the base type for all typed objects. It is not meant to be used directly. 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), x = ts_numeric(0),
result = ts_numeric(1) 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: # compile to:
# const out = { # const sampler = R.ocap(
# sample_one: R.ocap([R.as_vector(z.number())], R.numeric(1)), # [],
# }; # R.list({
# num: R.ocap([], R.numeric(1))
# })
# );