Using Julia inside targets

This vignette is the reference for running Julia scripts as targets steps with 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

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-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:

# `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:

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

R/post_result.R:

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

_targets.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

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)

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.

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

Use case 2: a specific juliaup version

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:

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

or per step:

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

Tracking scripts as dependencies

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

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