---
title: "Exports, internal routines and classes"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Exports, internal routines and classes}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
library(rzig)
compiler <- find_zig(required = FALSE)
has_zig <- isTRUE(compiler$supported)
```

The marker `/// @export` makes a public Zig function an R function. Options of
the marker and the documentation lines decide the name, the defaults and the
visibility of the R function; `/// @class` makes a struct an R class. This
vignette shows each form in one package. The scan and the generated files are
plain R; the build and the calls run when a supported Zig compiler is found
(this copy `r if (has_zig) "ran" else "skipped"` them).

```{r create}
path <- file.path(tempdir(), "exportspkg")
create_rzig_package(path, tests = FALSE)
main_zig <- file.path(path, "src", "rzig", "src", "main.zig")
```

## Names, defaults and invisible results

`name=` gives the R function another name than the Zig function, `@default`
gives an argument a default value written in R, and `invisible` returns the
result invisibly:

```{r options, comment = ""}
cat(
  "",
  "/// Shrink values towards zero.",
  "/// @param x A numeric vector.",
  "/// @param amount The amount to subtract from each absolute value.",
  "/// @default amount 0.5",
  "/// @return The shrunk values, invisibly.",
  "/// @export name=shrink invisible",
  "pub fn shrink_zig(ctx: *rzig.Ctx, x: []const f64, amount: f64) rzig.Error![]f64 {",
  "    const out = try ctx.alloc(f64, x.len);",
  "    for (x, out) |value, *slot| {",
  "        const size = @max(@abs(value) - amount, 0);",
  "        slot.* = if (value < 0) -size else size;",
  "    }",
  "    return out;",
  "}",
  sep = "\n", file = main_zig, append = TRUE
)
```

## Internal routines

A routine that only the package's own R code calls is marked
`/// @export internal`: it gets an R wrapper, which the package does not
export and which gets no help page. The exported R function checks its
arguments in R and calls it:

```{r internal, comment = ""}
cat(
  "",
  "/// The weighted mean, for weighted_mean().",
  "/// @export internal",
  "pub fn weighted_mean_zig(x: []const f64, w: []const f64) rzig.Error!f64 {",
  "    if (x.len != w.len) return rzig.raise(\"x and w differ in length\", .{});",
  "    var total: f64 = 0;",
  "    var weights: f64 = 0;",
  "    for (x, w) |value, weight| {",
  "        total += value * weight;",
  "        weights += weight;",
  "    }",
  "    return total / weights;",
  "}",
  sep = "\n", file = main_zig, append = TRUE
)
writeLines(c(
  "#' The weighted mean",
  "#'",
  "#' @param x A numeric vector.",
  "#' @param w Non-negative weights of the same length.",
  "#' @return A number.",
  "#' @export",
  "weighted_mean <- function(x, w = rep(1, length(x))) {",
  "  stopifnot(is.numeric(x), is.numeric(w), all(w >= 0))",
  "  weighted_mean_zig(as.double(x), as.double(w))",
  "}"
), file.path(path, "R", "weighted.R"))
```

## Classes

A struct marked `/// @class` becomes an R class. Its `init` function is the
constructor, exported under the struct's name, and each public function that
takes the struct as its first parameter is a method, called with `$`. The
object owns the Zig value, which R's garbage collector releases:

```{r class, comment = ""}
cat(
  "",
  "/// A running mean.",
  "/// @class",
  "pub const RunningMean = struct {",
  "    total: f64 = 0,",
  "    count: usize = 0,",
  "",
  "    /// Create an empty running mean.",
  "    pub fn init() RunningMean {",
  "        return .{};",
  "    }",
  "",
  "    /// Add values.",
  "    pub fn add(self: *RunningMean, values: []const f64) void {",
  "        for (values) |value| self.total += value;",
  "        self.count += values.len;",
  "    }",
  "",
  "    /// The mean of the values added so far.",
  "    pub fn mean(self: *const RunningMean) f64 {",
  "        return self.total / @as(f64, @floatFromInt(self.count));",
  "    }",
  "};",
  sep = "\n", file = main_zig, append = TRUE
)
```

## What R sees

The scan lists every export with its R arguments, the internal routine marked
as such, and the constructor and methods of the class:

```{r scan}
scan_exports(path)
```

`document()` writes the wrappers; the generated R code shows the default, the
invisible result and the internal routine:

```{r document}
bindings <- document(path)
cat(bindings$wrappers)
```

The `NAMESPACE` of the package has a block that `document()` manages; the
lines outside it belong to the package author, here the export of the R
function `weighted_mean()`:

```{r namespace}
cat("export(weighted_mean)\n", file = file.path(path, "NAMESPACE"), append = TRUE)
cat(readLines(file.path(path, "NAMESPACE")), sep = "\n")
```

## Build and use

```{r build, eval = has_zig}
build <- build_zig(path)
library(build$package, character.only = TRUE, lib.loc = build$library)
shrink(c(-2, 0.2, 3))
print(shrink(c(-2, 0.2, 3), amount = 1))
weighted_mean(c(1, 2, 3), c(1, 1, 2))
exists("weighted_mean_zig", envir = asNamespace("exportspkg"))
"weighted_mean_zig" %in% getNamespaceExports("exportspkg")
m <- RunningMean()
m$add(c(1, 2, 3))
m$add(10)
m$mean()
m
```

## Customized build files

The build files of the scaffold are templates that the package author may
extend, for example `src/Makevars.in` with additional flags. `update_rzig()`
keeps such a file and lists it, and `rzig_status()` notes it; the vignette on
updating rzig shows both. The framework files under `src/rzig/framework`
belong to rzig and are replaced by every update.

```{r cleanup, include = FALSE}
if (has_zig) unloadNamespace("exportspkg")
unlink(path, recursive = TRUE)
```
