---
title: "Differences to R"
author: "Konrad Kraemer"
output: html_document

vignette: >
  %\VignetteIndexEntry{Differences to R}
  %\VignetteEngine{knitr::rmarkdown_notangle}
  \usepackage{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, eval = FALSE)
library(ast2ast)
```

## Overview

ast2ast follows R wherever possible. A few things work differently because
the generated C++ is statically typed:

- variables from the enclosing environment are not captured
- vectors are not recycled
- `int` arguments need integer input (`200L`)
- `sum()` of an integer vector gives `NA` on overflow, not a double
- `Inf`, `NaN` and `NA` are created via `c()`

## No access to the enclosing environment

A translated function only sees its arguments and its own variables.
Everything else has to be passed as an argument.

```{r}
k <- 2
f <- function(x) {
  argtypes(x |> type(vec(double)))
  return(x * k)
}
translate(f)
# return(x * k)
# Found uninitialized variable: k
```

Fix: pass `k` as argument.

```{r}
f <- function(x, k) {
  argtypes(
    x |> type(vec(double)),
    k |> type(double)
  )
  return(x * k)
}
```

## No recycling

Both operands must have the same length, or one of them must be a scalar.
R would recycle the shorter vector.

```{r}
f <- function() {
  return(c(1, 2, 3, 4) + c(10, 20))
}
fcpp <- translate(f)
fcpp()
# In 'return(c(1.0, 2.0, 3.0, 4.0) + c(10.0, 20.0))': encountered non-conformable arrays
```

Fix: build the vector with the full length, e.g. `rep(c(10, 20), 2L)`.

## Integer arguments

An argument of type `int` only accepts integers. A double is not converted.

```{r}
f <- function(n) {
  argtypes(n |> type(int))
  return(n + 1L)
}
fcpp <- translate(f)
fcpp(200)
# Type mismatch for 'n': expected integer, got double
fcpp(200L)
# [1] 201
```

The same holds inside `f`: a bare `1` is a double. Use `1L` or `1L:3L` for
integers.

## Integer sum

Integer overflow gives `NA` with a warning, as in R. Only `sum()` differs:
R returns a double if the sum of an integer vector does not fit into an
integer. In ast2ast the type is fixed at translation time, so the result is
`NA` with a warning.

```{r}
f <- function(x) {
  argtypes(x |> type(vec(int)))
  return(sum(x))
}
translate(f)(c(.Machine$integer.max, 1L))
# [1] NA
# Warning: integer overflow - use sum(as.numeric(.))
```

## Inf, NaN and NA

A bare `Inf` or `NaN` as a scalar is rejected. Wrap the value in `c()`:

```{r}
f <- function() {
  y <- -Inf
  return(y)
}
translate(f)
# Found unallowed base type Inf, for the variable y

f <- function() {
  y <- c(-Inf)
  z <- c(1, Inf, NaN, NA)
  return(z)
}
translate(f)()
# [1]   1 Inf NaN  NA
```

As in R, `c(NA)` alone is logical; together with a double it becomes a
double `NA`.
