---
title: "Using Julia inside targets"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Using Julia inside targets}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE)
```

This vignette is the reference for running **Julia** scripts as `targets` steps with [JuliaCall](https://github.com/JuliaInterop/JuliaCall): every `tar_target_jl()` argument, how to choose the Julia install and project, and the common use cases. It mirrors `vignette("python")`: the *shape* of a Julia step is identical to a Python one, only the bridge and the environment arguments differ. For a gentler tour start with `vignette("get_started")`.

Code blocks are illustrative and **not executed** when the vignette builds.

## The two constructors

* `tar_target_jl()`: bare `name` (and unquoted `pattern`), for direct use in `_targets.R`.
* `tar_target_jl_raw()`: string `name`, for use inside targets factories.

Both return a single `targets` target and forward **every** `targets::tar_target_raw()` argument (`pattern`, `format`, `iteration`, `deployment`, `resources`, `cue`, ...).

## Arguments

### Scripts, data, and output

These behave exactly as for Python (see `vignette("python")`):

| Argument | Meaning |
|---|---|
| `script` | Path to the Julia script to run (**required**). Runs in the `Main` module. |
| `pre_script` | Optional R script run **before** the Julia script. Assign a named list `to_jl` to push objects into `Main`. |
| `post_script` | Optional R script run **after** the Julia script. `jl_get()` and `jl_call()` are available; its last expression is the value (object mode) or file paths (file mode). |
| `inputs` | Named vector mapping in-step names to upstream targets, e.g. `c(df = "prepared")`. |
| `output` | `"object"` (default) or `"file"`. |
| `retrieve` | Julia variable name(s) to return when there is no post-script (object mode). |
| `files` | Paths to return when there is no post-script (file mode). |

As with Python, any of `script` / `pre_script` / `post_script` may be a literal path or a `tar_target_path("name")` reference to track the file.

### Choosing the Julia install / project

| Argument | Meaning |
|---|---|
| `julia_version` | A version string (e.g. `"1.11"`), resolved to a [juliaup](https://github.com/JuliaLang/juliaup)-managed install. Used when `julia_home` is not given. |
| `julia_home` | The directory containing the `julia` executable. Defaults to `getOption("tarpolyglot.julia_home")`; when unset and no `julia_version`, JuliaCall discovers Julia on `PATH`. |
| `julia_project` | A Julia project environment (folder with `Project.toml` / `Manifest.toml`) to `Pkg.activate()`. When `NULL`, Julia's default global environment (`@v#.#`) is used. |
| `julia_packages` | Character vector of packages to `using` before running the script. |

## The three-script model

```
 upstream targets ─► pre_script (R) ─► script (.jl) ─► post_script (R) ─► target value
                     builds `to_jl`     computes         reads jl_get("name") /
                                        `result`         jl_call(fn, ...)
```

1. **`script`**: the Julia file, run in `Main`.
2. **`pre_script`**: the `inputs` are already bound by name. Assign a named list `to_jl`; each element is `julia_assign()`ed as a variable in `Main`.
3. **`post_script`**: JuliaCall has no `py`-style proxy, so you read variables back through `jl_get("name")` (a shortcut for `JuliaCall::julia_eval("name")`) and call Julia functions with `jl_call` (an alias of `JuliaCall::julia_call()`).

## Object output

`jl/stats.jl`:

```julia
# `x` was pushed from R by the pre-script.
seq = isa(x, AbstractVector) ? x : [x]
result = Dict("sum" => sum(seq), "n" => length(seq), "mean" => sum(seq) / length(seq))
```

`R/pre_push.R`:

```r
to_jl <- list(x = x)   # `x` came from inputs = c(x = "prepared_x")
```

`R/post_result.R`:

```r
res <- jl_get("result")
data.frame(sum = res$sum, n = res$n, mean = res$mean)   # last expression = value
```

`_targets.R`:

```r
library(targets)
library(tarpolyglot)

list(
  tar_target(prepared_x, c(1, 2, 3, 4)),

  # (a) return a Julia variable directly with `retrieve` (no post-script)
  tar_target_jl(
    name = jl_direct,
    script = "jl/stats.jl",
    inputs = c(x = "prepared_x"),
    pre_script = "R/pre_push.R",
    retrieve = "result"
  ),

  # (b) reshape the result in a post-script
  tar_target_jl(
    name = jl_prepost,
    script = "jl/stats.jl",
    inputs = c(x = "prepared_x"),
    pre_script = "R/pre_push.R",
    post_script = "R/post_result.R"
  )
)
```

## File output

```r
tar_target_jl(
  name = jl_file,
  script = "jl/write.jl",            # writes a file, stores its path in `out_path`
  inputs = c(x = "prepared_x"),
  pre_script = "R/pre_push.R",
  post_script = "R/post_files.R",    # returns jl_get("out_path")
  output = "file"
)
```

## Dynamic branching (iris example)

```r
list(
  tar_target(iris_groups, split(iris, iris$Species), iteration = "list"),
  tar_target_jl(
    name = fit_by_group,
    script = "jl/fit.jl",
    inputs = c(df = "iris_groups"),
    pre_script = "R/pre_push_jl.R",  # to_jl <- list(df = df)
    retrieve = "result",
    pattern = map(iris_groups),      # one branch per species
    iteration = "list"
  )
)
```

## Choosing the Julia install and project

### Use case 1: Julia on PATH (default)

Set nothing; JuliaCall discovers the `julia` on `PATH` and uses the global environment.

```r
tar_target_jl(
  name = probe,
  script = "jl/probe.jl",
  retrieve = "result"
)
```

### Use case 2: a specific juliaup version

```r
tar_target_jl(
  name = fit,
  script = "jl/fit.jl",
  julia_version = "1.11",            # resolved via juliaup
  retrieve = "result"
)
```

### Use case 3: an explicit Julia home

If discovery fails (e.g. a fresh install not yet on `PATH`, or the Windows juliaup shim), point at the `bin` directory. You can set it once globally:

```r
options(tarpolyglot.julia_home = "C:/Users/me/.julia/juliaup/.../bin")
```

or per step:

```r
tar_target_jl(
  name = fit,
  script = "jl/fit.jl",
  julia_home = "C:/Users/me/.julia/juliaup/.../bin",
  retrieve = "result"
)
```

### Use case 4: a pinned project (recommended)

For reproducibility, activate a Julia **project** with a committed `Manifest.toml`, and `using` the packages the script needs:

```r
tar_target_jl(
  name = solve,
  script = "jl/solve.jl",
  julia_project = "julia/MyEnv",     # folder with Project.toml + Manifest.toml
  julia_packages = c("LinearAlgebra", "Statistics"),
  retrieve = "result"
)
```

The requested project takes priority over an ambient `JULIA_PROJECT` environment variable (e.g. one inherited by `crew` workers): it is cleared for the duration of the Julia binding, so your explicit `julia_project` (or the global environment you get when none is given) wins.

> **One interpreter per session.** JuliaCall binds a single Julia per R session, so all Julia targets that run in the same session share one project and set of loaded packages. To use different projects in one pipeline, run those targets on separate `crew` workers (see `vignette("get_started")`).

## Tracking scripts as dependencies

```r
list(
  tar_target(fit_jl, "jl/fit.jl", format = "file"),

  tar_target_jl(
    name = fit,
    script = tar_target_path("fit_jl"),   # re-runs when jl/fit.jl changes
    inputs = c(x = "data"),
    retrieve = "result"
  )
)
```

## Conversion caveats

* Julia is **1-indexed** (unlike Python).
* Check the JuliaCall conversion rules for which R/Julia types round-trip; for anything that does not, use file mode.

See `?tar_target_jl` and `?run_jl_step` for the full argument reference.
