basic attempt at creating overloaded functions
This commit is contained in:
parent
e641b139b8
commit
c65ac9c3a1
@ -4,6 +4,8 @@ S3method(print,ts_object)
|
|||||||
S3method(ts_compile,character)
|
S3method(ts_compile,character)
|
||||||
S3method(ts_compile,default)
|
S3method(ts_compile,default)
|
||||||
S3method(ts_compile,ts_function)
|
S3method(ts_compile,ts_function)
|
||||||
|
S3method(ts_compile,ts_overload)
|
||||||
|
export(is_overload)
|
||||||
export(ts_character)
|
export(ts_character)
|
||||||
export(ts_compile)
|
export(ts_compile)
|
||||||
export(ts_dataframe)
|
export(ts_dataframe)
|
||||||
@ -13,3 +15,4 @@ export(ts_integer)
|
|||||||
export(ts_list)
|
export(ts_list)
|
||||||
export(ts_logical)
|
export(ts_logical)
|
||||||
export(ts_numeric)
|
export(ts_numeric)
|
||||||
|
export(ts_overload)
|
||||||
|
|||||||
15
R/compile.R
15
R/compile.R
@ -1,10 +1,10 @@
|
|||||||
#' @export
|
#' @export
|
||||||
ts_compile <- function(f, file = NULL, ...) {
|
ts_compile <- function(f, ..., file = NULL) {
|
||||||
UseMethod("ts_compile")
|
UseMethod("ts_compile")
|
||||||
}
|
}
|
||||||
|
|
||||||
#' @export
|
#' @export
|
||||||
ts_compile.ts_function <- function(f, file = NULL, name = deparse(substitute(f))) {
|
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")
|
||||||
|
|
||||||
@ -14,10 +14,17 @@ ts_compile.ts_function <- function(f, file = NULL, name = deparse(substitute(f))
|
|||||||
sprintf("const %s = (%s) => Promise<%s>;", name, fn_args, result$type_fn)
|
sprintf("const %s = (%s) => Promise<%s>;", name, fn_args, result$type_fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#' @export
|
||||||
|
ts_compile.ts_overload <- function(f, file = NULL, name = deparse(substitute(f))) {
|
||||||
|
cmt <- sprintf("\n// %s overloads", name)
|
||||||
|
oloads <- sapply(f, ts_compile, name = name)
|
||||||
|
paste(cmt, paste(oloads, collapse = "\n"), sep = "\n")
|
||||||
|
}
|
||||||
|
|
||||||
#' @export
|
#' @export
|
||||||
ts_compile.character <- function(
|
ts_compile.character <- function(
|
||||||
f,
|
f,
|
||||||
file = sprintf("%s.ts", tools::file_path_sans_ext(f))) {
|
file = sprintf("%s.d.ts", tools::file_path_sans_ext(f))) {
|
||||||
if (length(f) > 1) {
|
if (length(f) > 1) {
|
||||||
return(sapply(f, ts_compile))
|
return(sapply(f, ts_compile))
|
||||||
}
|
}
|
||||||
@ -53,6 +60,6 @@ ts_compile.character <- function(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#' @export
|
#' @export
|
||||||
ts_compile.default <- function(f) {
|
ts_compile.default <- function(f, ...) {
|
||||||
warning("Not supported")
|
warning("Not supported")
|
||||||
}
|
}
|
||||||
|
|||||||
30
R/function.R
30
R/function.R
@ -14,13 +14,27 @@ parse_args <- function(x, mc) {
|
|||||||
args
|
args
|
||||||
}
|
}
|
||||||
|
|
||||||
# TS function
|
#' TS function definition
|
||||||
|
#'
|
||||||
|
#' @param f an R function
|
||||||
|
#' @param ... argument definitions, OR function overloads
|
||||||
|
#' @param result return type (ignored if overloads are provided)
|
||||||
#' @export
|
#' @export
|
||||||
ts_function <- function(f, ..., result = NULL) {
|
ts_function <- function(f, ..., result = NULL) {
|
||||||
args <- list(...)
|
args <- list(...)
|
||||||
if (!is_object(result)) {
|
if (!is.null(result) && !is_object(result)) {
|
||||||
stop("Invalid return type")
|
stop("Invalid return type")
|
||||||
}
|
}
|
||||||
|
if (any(is_overload(args))) {
|
||||||
|
if (!all(is_overload(args))) {
|
||||||
|
stop("Cannot mix overloads with standard arguments")
|
||||||
|
}
|
||||||
|
z <- lapply(args, function(x) {
|
||||||
|
do.call(ts_function, c(list(f), x$args, list(result = x$result)))
|
||||||
|
})
|
||||||
|
class(z) <- "ts_overload"
|
||||||
|
return(z)
|
||||||
|
}
|
||||||
|
|
||||||
fn <- function(...) {
|
fn <- function(...) {
|
||||||
mc <- match.call(f)
|
mc <- match.call(f)
|
||||||
@ -32,3 +46,15 @@ ts_function <- function(f, ..., result = NULL) {
|
|||||||
class(fn) <- c("ts_function", class(f))
|
class(fn) <- c("ts_function", class(f))
|
||||||
fn
|
fn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#' @export
|
||||||
|
is_overload <- function(x) {
|
||||||
|
sapply(x, inherits, what = "ts_overload")
|
||||||
|
}
|
||||||
|
|
||||||
|
#' @export
|
||||||
|
ts_overload <- function(..., result = NULL) {
|
||||||
|
structure(list(args = list(...), result = result),
|
||||||
|
class = "ts_overload"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@ -6,12 +6,14 @@
|
|||||||
#' @param type_fn The type of the object that Typescript expects to recieve from R.
|
#' @param type_fn The type of the object that Typescript expects to recieve from R.
|
||||||
#' @param default The default value of the object.
|
#' @param default The default value of the object.
|
||||||
#' @param 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.
|
#' @param 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.
|
||||||
|
#' @param generic logical, if `TRUE` then the object is a generic type.
|
||||||
#'
|
#'
|
||||||
#' @md
|
#' @md
|
||||||
object <- function(type = "any",
|
object <- function(type = "any",
|
||||||
type_fn = "any",
|
type_fn = "any",
|
||||||
default = NULL,
|
default = NULL,
|
||||||
check = function() stop("Not implemented")) {
|
check = function() stop("Not implemented"),
|
||||||
|
generic = FALSE) {
|
||||||
e <- environment()
|
e <- environment()
|
||||||
|
|
||||||
e$attr <- function(name, value) {
|
e$attr <- function(name, value) {
|
||||||
|
|||||||
@ -102,9 +102,9 @@ myfun(1:5)
|
|||||||
|
|
||||||
myfun("hello world")
|
myfun("hello world")
|
||||||
|
|
||||||
cat(readLines("tests/testthat/app/app.R"), sep = "\n")
|
cat(readLines("tests/testthat/app.R"), sep = "\n")
|
||||||
|
|
||||||
ts_compile("tests/testthat/app/app.R", file = "")
|
ts_compile("tests/testthat/app.R", file = "")
|
||||||
```
|
```
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
@ -114,3 +114,5 @@ ts_compile("tests/testthat/app/app.R", file = "")
|
|||||||
- [ ] Add support for conditional return types
|
- [ ] Add support for conditional return types
|
||||||
|
|
||||||
e.g., `const sample = <T, N extends number>(x: T[], n: N) => N extends 1 ? T : T[]`
|
e.g., `const sample = <T, N extends number>(x: T[], n: N) => N extends 1 ? T : T[]`
|
||||||
|
|
||||||
|
- [ ] Function overloads? Perhaps just a wrapper around several function definitions...
|
||||||
|
|||||||
23
README.md
23
README.md
@ -98,19 +98,35 @@ myfun(1:5)
|
|||||||
myfun("hello world")
|
myfun("hello world")
|
||||||
#> Error: Expected a number
|
#> Error: Expected a number
|
||||||
|
|
||||||
cat(readLines("tests/testthat/app/app.R"), sep = "\n")
|
cat(readLines("tests/testthat/app.R"), sep = "\n")
|
||||||
#> library(ts)
|
#> library(ts)
|
||||||
#>
|
#>
|
||||||
#> fn_mean <- ts_function(mean, x = ts_numeric(), result = ts_numeric(1))
|
#> fn_mean <- ts_function(mean, x = ts_numeric(), result = ts_numeric(1))
|
||||||
#> fn_first <- ts_function(function(x) x[1],
|
#> fn_first <- ts_function(function(x) x[1],
|
||||||
#> x = ts_character(-1), result = ts_character(1)
|
#> x = ts_character(-1), result = ts_character(1)
|
||||||
#> )
|
#> )
|
||||||
|
#>
|
||||||
|
#> sample_one <- ts_function(
|
||||||
|
#> sample,
|
||||||
|
#> ts_overload(
|
||||||
|
#> x = ts_numeric(),
|
||||||
|
#> result = ts_numeric(1)
|
||||||
|
#> ),
|
||||||
|
#> ts_overload(
|
||||||
|
#> x = ts_character(),
|
||||||
|
#> result = ts_character(1)
|
||||||
|
#> )
|
||||||
|
#> )
|
||||||
|
|
||||||
ts_compile("tests/testthat/app/app.R", file = "")
|
ts_compile("tests/testthat/app.R", file = "")
|
||||||
#> import type { Character, Numeric } from 'rserve-ts';
|
#> import type { Character, Numeric } from 'rserve-ts';
|
||||||
#>
|
#>
|
||||||
#> const fn_first = (x: string | string[]) => Promise<Character<1>)>;
|
#> const fn_first = (x: string | string[]) => Promise<Character<1>)>;
|
||||||
#> const fn_mean = (x: number | number[]) => Promise<Numeric<1>)>;
|
#> const fn_mean = (x: number | number[]) => Promise<Numeric<1>)>;
|
||||||
|
#>
|
||||||
|
#> // sample_one overloads
|
||||||
|
#> const sample_one = (x: number | number[]) => Promise<Numeric<1>)>;
|
||||||
|
#> const sample_one = (x: string | string[]) => Promise<Character<1>)>;
|
||||||
```
|
```
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
@ -123,3 +139,6 @@ ts_compile("tests/testthat/app/app.R", file = "")
|
|||||||
|
|
||||||
e.g., `const sample = <T, N extends number>(x: T[], n: N) => N
|
e.g., `const sample = <T, N extends number>(x: T[], n: N) => N
|
||||||
extends 1 ? T : T[]`
|
extends 1 ? T : T[]`
|
||||||
|
|
||||||
|
- [ ] Function overloads? Perhaps just a wrapper around several
|
||||||
|
function definitions…
|
||||||
|
|||||||
@ -8,7 +8,8 @@ object(
|
|||||||
type = "any",
|
type = "any",
|
||||||
type_fn = "any",
|
type_fn = "any",
|
||||||
default = NULL,
|
default = NULL,
|
||||||
check = function() stop("Not implemented")
|
check = function() stop("Not implemented"),
|
||||||
|
generic = FALSE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
\arguments{
|
\arguments{
|
||||||
@ -19,6 +20,8 @@ object(
|
|||||||
\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.}
|
||||||
}
|
}
|
||||||
\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.
|
||||||
|
|||||||
18
man/ts_function.Rd
Normal file
18
man/ts_function.Rd
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
% Generated by roxygen2: do not edit by hand
|
||||||
|
% Please edit documentation in R/function.R
|
||||||
|
\name{ts_function}
|
||||||
|
\alias{ts_function}
|
||||||
|
\title{TS function definition}
|
||||||
|
\usage{
|
||||||
|
ts_function(f, ..., result = NULL)
|
||||||
|
}
|
||||||
|
\arguments{
|
||||||
|
\item{f}{an R function}
|
||||||
|
|
||||||
|
\item{...}{argument definitions, OR function overloads}
|
||||||
|
|
||||||
|
\item{result}{return type (ignored if overloads are provided)}
|
||||||
|
}
|
||||||
|
\description{
|
||||||
|
TS function definition
|
||||||
|
}
|
||||||
18
tests/testthat/app.R
Normal file
18
tests/testthat/app.R
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
library(ts)
|
||||||
|
|
||||||
|
fn_mean <- ts_function(mean, x = ts_numeric(), result = ts_numeric(1))
|
||||||
|
fn_first <- ts_function(function(x) x[1],
|
||||||
|
x = ts_character(-1), result = ts_character(1)
|
||||||
|
)
|
||||||
|
|
||||||
|
sample_one <- ts_function(
|
||||||
|
sample,
|
||||||
|
ts_overload(
|
||||||
|
x = ts_numeric(),
|
||||||
|
result = ts_numeric(1)
|
||||||
|
),
|
||||||
|
ts_overload(
|
||||||
|
x = ts_character(),
|
||||||
|
result = ts_character(1)
|
||||||
|
)
|
||||||
|
)
|
||||||
5
tests/testthat/app.d.ts
vendored
Normal file
5
tests/testthat/app.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import type { Character, Numeric, PE.Numeric<1>, } from 'rserve-ts';
|
||||||
|
|
||||||
|
const fn_first = (x: string | string[]) => Promise<Character<1>)>;
|
||||||
|
const fn_mean = (x: number | number[]) => Promise<Numeric<1>)>;
|
||||||
|
c("const sample_one = (x: number | number[]) => Promise<Numeric<1>)>;", "const sample_one = (x: string | string[]) => Promise<Character<1>)>;")
|
||||||
@ -1,6 +0,0 @@
|
|||||||
library(ts)
|
|
||||||
|
|
||||||
fn_mean <- ts_function(mean, x = ts_numeric(), result = ts_numeric(1))
|
|
||||||
fn_first <- ts_function(function(x) x[1],
|
|
||||||
x = ts_character(-1), result = ts_character(1)
|
|
||||||
)
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
import { types } from 'rserve-ts';
|
|
||||||
|
|
||||||
const fn_first = (x: string | string[]) => Promise<RTYPE.Character<1>)>;
|
|
||||||
const fn_mean = (x: number | number[]) => Promise<RTYPE.Numeric<1>)>;
|
|
||||||
17
tests/testthat/functions.R
Normal file
17
tests/testthat/functions.R
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# overload input/return types
|
||||||
|
sample_one <- ts_function(
|
||||||
|
sample,
|
||||||
|
ts_overload(
|
||||||
|
x = ts_numeric(),
|
||||||
|
result = ts_numeric(1)
|
||||||
|
),
|
||||||
|
ts_overload(
|
||||||
|
x = ts_character(),
|
||||||
|
result = ts_character(1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
ts_compile(sample_one)
|
||||||
|
|
||||||
|
# compile to:
|
||||||
|
# const sample_one = (x: number) => Promise<number>;
|
||||||
|
# const sample_one = (x: string) => Promise<string>;
|
||||||
Loading…
x
Reference in New Issue
Block a user