Skip to content

Modules and imports ​

Docker for the last task only. This topic covers named imports with as, namespace imports, internal, re-export, and import command, which brings in the command words another module declares.

bash
lask eval page-title "  Release Notes  "
lask eval known-targets
lask sync && lask eval ship --version 1.4.0

The thing to take away: a named or namespace import brings none of a module's command words with it — only import command does, and dispatch is resolved where the command is written.

This topic has 3 files, so clone the repository to try it:

bash
git clone https://github.com/lask-task-runner/lask.git
cd lask/example/02-language/08-modules

Source ​

These are example/02-language/08-modules in the lask repository.

lask
// Modules and imports
//
// One file is one module. Inside a project, modules import each other
// by relative path; across projects, they are pinned dependencies,
// which 09-dependencies shows.
//
//   lask check
//   lask eval page_title "  Release Notes  "
//   lask sync && lask eval ship

// Named import. `as` renames, and a type comes across the same way a
// function does.
import { slugify, titleize as to_title, Slug } from "./lib/text.lask"

// Namespace import: everything the module exports, under one name.
import * as deploy from "./lib/deploy.lask"

// Command import: `$ echo` in this module now runs where the library
// declared it. Command words come across only by name, so an import of
// any other kind never changes where a command runs.
import command { "echo" } from "./lib/deploy.lask"

// Re-export: `ship_to` becomes a public symbol of *this* module, with
// the parameter list of the original intact — keyword parameters,
// defaults and arity all preserved, unlike `f = m.f`, which would make
// a function value and lose them. It is how a library whose public API
// spans several files publishes all of it from one entry module.
export { rollout as ship_to } from "./lib/deploy.lask"

// Turn free text into a page title.
//
// `internal` is not importable, so `dashes` and `capitalize` inside
// lib/text.lask cannot be reached from here — only what the library
// chose to publish.
//
// @example lask eval page-title "  Release Notes  "
page_title(raw: String): String = do {
  s: Slug = slugify(raw)
  to_title(s)
}

// Every target the deploy library knows about.
//
// Imported values are ordinary values.
//
// @return Every target the deploy library knows about.
known_targets(): Array<String> = deploy.targets

// Roll a version out to every target.
//
// A call into another module is an ordinary call. The command inside
// `rollout` runs in the environment *that* module declared — dispatch
// is resolved where the command is written.
//
// @example lask run ship --version 1.4.0
ship(--version: String = "1.4.0"): Array<String> =
  for (t: deploy.targets) { deploy.rollout(t, version = version) }

// Announce a release, with the imported `echo`.
//
// @example lask eval announce --version 1.4.0
announce(--version: String = "1.4.0"): String = $ echo "releasing #{version}"
  |> trim

// The import graph has to be acyclic, and an import of an external
// dependency reaches only that dependency's root main.lask — never a
// file inside it.
lask
// A second library, this one running commands.
//
// A command declaration is exported unless it is marked `internal`,
// but it governs another module only once that module names the word
// in `import command { ... }` — main.lask does, for "echo". A named or
// a namespace import brings in no command word.
command { "echo" } on #alpine:3.22.2

// @param target   Where to roll out to.
// @param version  What to roll out.
rollout(target: String, --version: String = "latest"): String = do {
  out = $ echo "rolling out #{version} to #{target}"
  trim(out)
}

// A module can expose values, not just functions.
targets: Array<String> = ["staging", "production"]
lask
// A library module. One file is one module, and every top-level
// declaration is public unless it is marked `internal`.

// A type alias travels across the import boundary like any other name.
type Slug = String

// @param s  Free text to turn into a slug.
slugify(s: String): Slug =
  s |> trim |> to_lower |> dashes

// `internal` keeps this one inside the module: importing it elsewhere
// is a static error, and it stays out of `lask`'s listings.
internal dashes(s: String): String = replace(s, " ", "-")

titleize(s: String): String = do {
  parts = split(s, "-")
  join(map(parts, capitalize), " ")
}

internal capitalize(w: String): String =
  concat(to_upper(substring(w, 0, 1)), substring(w, 1, length(w)))

Released under the MIT License.