API Reference
Model Definition
StanBlocks.@slic Macro
Defines SlicModels (see test/slic.jl for usage examples).
The defining module is captured automatically via __module__, so that @deffun functions defined in the same module (e.g. a package extension) are found during symbol resolution.
A leading string literal inside the begin ... end block is captured as the model docstring and rendered as a // ... comment header in the generated Stan code.
StanBlocks.@deffun Macro
@deffun function_definitionDefine a Stan-compatible function with type inference and code generation.
Parses a Julia-style function definition (with type-annotated arguments and return type), generates the corresponding Stan function, and registers type-inference signatures so the transpiler can propagate types through calls to this function.
For functions ending in _lpdf/_lpmf/_lcdf/_lccdf, the return type is automatically set to real and companion _lpdfs/_rng stubs are generated for use in generated_quantities.
UDF bodies must not contain ~ sampling statements or target += increments — UDFs cannot introduce parameters or directly manipulate the log density. The macro errors at expansion time if either is found.
Standard Julia macros inside the body are expanded against the calling module before tracing, so @views, @., @inbounds, and user-defined macros work transparently.
Inlining
Annotating with @inline (or giving the function a Julia-convention trailing ! in its name) causes every call to be expanded at the call site instead of producing a Stan function:
@deffun @inline scale(x::vector[n], s::real)::vector[n] = x * s
@deffun set_first!(buf::vector[n])::vector[n] = (buf[1] = 42.; buf)Inline UDFs do not appear in Stan's functions {} block. Multi-statement bodies, vararg parameters, and higher-order function arguments are all supported. Locals are renamed per call site, and pre-statements hoist into the enclosing block.
@inline cannot be combined with @lhs / @lpxf.
Example
@deffun garch11_lpdf(y::vector[T], mu::real, alpha0::real, alpha1::real, beta1::real)::real = begin
sigma2 = alpha0
rv = 0.
for t in 1:T
rv += normal_lpdf(y[t], mu, sqrt(sigma2))
sigma2 = alpha0 + alpha1 * square(y[t] - mu) + beta1 * sigma2
end
return rv
endSee src/slic_stan/builtin.jl for many more examples.
StanBlocks.@defsig Macro
Utility macro to define function signatures (see src/slic_stan/builtin.jl for usage examples).
Note:
This macro is mainly useful for bulk built-in function signature definitions. StanBlocks.jl users should generally prefer using @deffun.
sourceStanBlocks.@usertype Macro
@usertype struct RaggedVector
mem :: vector
ends :: int[]
endDeclare a custom Stan-renderable record type. Lowers to a real Julia struct whose abstract supertype is StanBlocks.stan.types.usertype (added automatically); field type annotations are SLIC types and are kept only for documentation — fields are stored as Any so plain Julia construction works for data plumbing. Method dispatch on the type tag (Base.length(r::RaggedVector)) works via standard Julia. Stan-side, values render as positional tuples; field access (r.mem) reuses the existing ntup machinery.
Sampling-form Dispatch
StanBlocks.@lpxf Macro
@lpxf foo_lpdf
@lpxf begin foo_lpdf; bar_lpmf endRegister the three SLIC dispatch hooks (lpxf_expr, rng_expr, likelihood_expr) for one or more user-defined log-probability functions.
The argument(s) must be bare symbols ending in _lpdf, _lpmf, _lcdf, or _lccdf. For each foo_lpdf (or _lpmf/etc.), the macro emits the registrations:
StanBlocks.lpxf_expr(::typeof(foo)) = foo_lpdf
StanBlocks.rng_expr(::typeof(foo)) = foo_rng
StanBlocks.likelihood_expr(::typeof(foo)) = foo_lpdfsThe companion foo_rng and foo_lpdfs (resp. _lpmfs/_lcdfs/_lccdfs) names must already exist when the registrations execute. This macro does not parse function bodies and does not wrap @deffun.
StanBlocks.@lhs Macro
@lhs foo_lpdf(y::T, args...) = bodyInside a @deffun block, opt this method into base-level LHS inference. Without @lhs, only the _lpdf-keyed tracetype is registered (so the method dispatches when called explicitly), but lhs ~ foo(args...) cannot trace because the base foo has no tracetype keyed on its argument signature. @lhs registers tracetype(::CanonicalExpr{<:typeof(foo), <:Tuple{lhs_type[2:end]...}}) so the sampling form works.
Compose with @lpxf (any order — @lhs @lpxf … or @lpxf @lhs …) to also register the dispatch hooks for foo/foo_rng/foo_lpdfs.
Standalone @lhs (outside @deffun) is not supported and errors immediately.
Runtime Assertions
StanBlocks.@stan_assert Macro
@stan_assert cond
@stan_assert cond messageStan-compatible runtime assertion. Expands to if !cond; reject(msg); end, where Stan's reject aborts the current MCMC proposal with the message. Without an explicit message, a default "assertion failed: <cond>" is used.
Use inside @deffun bodies (control flow is not allowed in @slic model bodies — wrap the check in a helper if needed at the model level).
Example
@deffun safe_log(x::real)::real = begin
@stan_assert x > 0 "safe_log: argument must be positive"
return log(x)
endModel Inspection and Compilation
StanBlocks.stan_code Function
stan_code(model) -> StringReturn the generated Stan source for model (a SlicModel or StanModel) as a plain String.
For a SlicModel, tracing runs first via stan_model; for an already-traced StanModel, only the rendering pass runs. Output covers the full Stan program — data, transformed_data, parameters, transformed_parameters, model, and generated_quantities blocks — with automatic block placement applied.
Pair with transpiles for boolean smoke tests and stan_instantiate to compile the generated code via BridgeStan.
StanBlocks.stan_model Function
stan_model(slic::SlicModel) -> StanModelTrace slic end-to-end (forward / backward / distribute passes), returning the inferred StanModel — a fully resolved representation of the Stan blocks plus the data dictionary.
Tracing is the expensive step. A StanModel is cheap to re-data: call model(; new_kwargs...) to swap the data dict without re-tracing. Use this in preference to repeatedly calling stan_instantiate on the original SlicModel.
Errors during tracing are wrapped in a StanBlocksError tagged with phase = :transpile.
StanBlocks.stan_instantiate Function
stan_instantiate(model; kwargs...) -> StanProblemExported alias for StanBlocks.instantiate. Compiles model (a SlicModel or StanModel) via BridgeStan and returns a StanLogDensityProblems.StanProblem implementing the LogDensityProblems interface. See instantiate for the full kwarg list.
StanBlocks.instantiate Function
instantiate(model; nan_on_error=true, make_args=["STAN_THREADS=true"], warn=false, path=…) -> StanProblem
stan_instantiate(model; ...) -> StanProblemCompile model (a SlicModel or StanModel) via BridgeStan and return a StanLogDensityProblems.StanProblem. The returned value implements the LogDensityProblems interface — call LogDensityProblems.dimension, logdensity, and logdensity_and_gradient on it.
stan_instantiate is an exported alias for instantiate.
Keyword arguments
path::AbstractString— where to write the.stanfile. Defaults to"tmp/<hash>.stan", so identical generated code is cached on disk.nan_on_error::Bool = true— make BridgeStan returnNaNinstead of throwing on evaluation failures.make_args::Vector{String} = ["STAN_THREADS=true"]— extra arguments forwarded to Stan'smake.warn::Bool = false— forwarded to BridgeStan.
Errors during compilation are wrapped in a StanBlocksError tagged with phase = :compile.
Smoke Tests
StanBlocks.transpiles Function
transpiles(model; re=true) -> BoolReturn true if model (a SlicModel or StanModel) successfully transpiles to Stan source via stan_code, false otherwise.
This is the fast smoke-test for a model — it exercises the full SLIC tracing pipeline but stops short of invoking stanc / BridgeStan.
Set re=false to swallow the transpilation error and just return false (useful for batch regression dashboards); the default re=true rethrows so failures surface with their normal StanBlocksError trace.
StanBlocks.compiles Function
compiles(model; re=true) -> BoolReturn true if model (a SlicModel or StanModel) successfully transpiles and compiles via BridgeStan (i.e. stan_instantiate succeeds), false otherwise.
Strictly stronger than transpiles: a model that transpiles can still fail to compile if stanc rejects the generated Stan or the C++ build fails.
Set re=false to swallow the error and just return false; the default re=true rethrows.
StanBlocks.stanc_check Function
stanc_check(stan_code::AbstractString; warn_pedantic=true) -> (ok::Bool, output::String)Run the stanc compiler on stan_code (written to a temporary file) and return whether it accepted the code plus any compiler output (warnings, errors). Binary discovery: $STANC_PATH env var → BridgeStan's bin/stanc. Errors if neither is available.
Types
StanBlocks.SlicModel Type
The AST and the data, pre-tracing. Can be instantiated via stan_instantiate.
The mod field stores the defining module (set automatically by @slic), used for symbol resolution during tracing — functions defined via @deffun in package extensions are found by checking mod before falling back to Main.
Warning:
Repeatedly instantiating SlicModels is inefficient, as the tracing is redone for every instantiation. Instead, get the StanModel first (via model = stan_model(slic_model)) and update its data (via new_model = model(;x=new_x)).
StanBlocks.StanModel Type
The inferred Stan model, post-tracing. Can be instantiated via stan_instantiate.
Errors
StanBlocks.StanBlocksError Type
StanBlocksError <: ExceptionWraps errors that occur during transpilation, compilation, or evaluation of Stan models.
Fields
phase::Symbol: the pipeline stage where the error occurred (:transpile,:compile, or:evaluate)context::String: a description of what was being processed (e.g."model: eight_schools")cause::Any: the underlying error, typically an(exception, backtrace)tuple