Functions

Libraries

Reusable kScript libraries: the library() header, import with aliases and version pinning, precedence rules, sandboxing, and hot reload.

Advanced 10 min read

Introduction

Libraries let you write a function and type collection once and import it from any script: your TA helpers, your zone structs, your formatting utilities. A library is itself kScript, runs under exactly the same sandbox, and hot-reloads while you develop it.

Writing a library

A library is a source whose first statement is a library() header, followed by only function and type declarations:

library("my_ta", "1.0.0")

type Band {
  basis: number,
  upper: number,
  lower: number
}

func zscore(src, period) {
  var m = sma(source=src, period=period)
  var sd = stdev(source=src, period=period)
  return (src - m) / sd
}

func makeBand(src, period, mult) {
  var m = sma(source=src, period=period)
  var sd = stdev(source=src, period=period)
  return Band.new(basis=m, upper=m + sd * mult, lower=m - sd * mult)
}
  • The name must be lowercase ([a-z][a-z0-9_]*); the version is optional semver.
  • Anything else at top level (define, plots, input, data sources, bare expressions) is a compile error with line/column: libraries declare, consumers run.

Importing

import "my_ta"                    // latest version, plain names: zscore(...)
import "my_ta@1.0.0"              // pinned to an exact version
import "my_ta" as ta              // aliased: ta.zscore(...), ta.Band.new(...)
  • Plain import brings the library's functions and types in under their own names.
  • Alias import namespaces everything behind the alias, like the built-in math. namespace. Aliased imports can never collide with anything.
  • Pinning: name@version selects an exact registered version; a bare name takes the latest. Unknown names and versions error with the list of what is available.
  • import is contextual (old scripts using the word as an identifier keep parsing), and duplicate imports of the same library are reported as exactly that.

Precedence and integrity rules

Worked out so that nothing ever changes meaning silently:

  1. Your script wins. If a plain import collides with a function the script itself defines, the script's version is used at the script's call sites, and the compiler emits a warning telling you so.
  2. Libraries are internally sealed. A library function calling its sibling always gets its own sibling, even when the consumer overrides that name. Imports cannot be middleman-ed.
  3. Import-vs-import plain collisions are errors (ambiguity has no safe default); alias one of them.
  4. Aliases are validated: as math (a builtin namespace) or as close (a global) is a compile error.

Sandbox and attribution

Library code is ordinary kScript: every limit in the sandbox manifest applies unchanged, and errors raised inside a library carry the library's own line and column, so a runaway loop in my_ta says so, not some opaque consumer position.

Versioning and updates

Registering a new version of a library leaves existing pins untouched: scripts pinned to @1.0.0 keep their behavior; bare imports pick up the latest on their next run. Re-registering the same version is an error: versions are immutable once published.

Hot reload in the playground

The playground has a library editor alongside the script editor. Edit the library, run the script, and the new library code is picked up immediately: same page, no reload, registry updated in place. The development loop for a shared helper is exactly the development loop for a script.

Pine comparison

Pine libraries exist but are publish-bound (TradingView-hosted, review queues, no local hot iteration) and export only functions over Pine's types. kScript libraries are local-first, version-pinned, hot-reloadable, can export your own structured types, and their consumers keep every compile-time guarantee (typed fields, precedence warnings, sandbox attribution) the rest of v3 provides.