Skip to content

Libraries

A Library bundles operators, clauses, primitives and functions into a named, loadable unit a Solver can use. Multiple libraries can be loaded at once, grouped into a Runtime.

Pluggable

Library extends Pluggable (it.unibo.tuprolog.solve.library), which defines what a library actually contributes:

/**
 * Base type for anything that can be "plugged into" a solver by contributing operators, clauses, primitives, and
 * functions -- namely [Library] and [Runtime] (a group of libraries), which both implement this by aggregating
 * their constituents' contributions.
 */
interface Pluggable {
    /** Operators to be loaded by a solver when the library is used */
    @JsName("operators")
    val operators: OperatorSet

    /** Rules, facts, or directories to be loaded by a solver when the library is used */
    @JsName("clauses")
    val clauses: List<Clause>

    /** [Primitive]s to be loaded by a solver when the library is used,
     * indexed by their [Signature] in the eyes of the solver */
    @JsName("primitives")
    val primitives: Map<Signature, Primitive>
  • operators: OperatorSet — operators to register with the solver;
  • clauses: List<Clause> — facts/rules/directives to load into the theory (see Default predicates for an example);
  • primitives: Map<Signature, Primitive> and functions: Map<Signature, LogicFunction> — see Primitives and functions.

Pluggable also provides default lookup helpers: contains(signature), contains(operator), hasPrimitive, hasFunction, hasRule, rulesSignatures (derived from clauses), and hasProtected (signatures a library reserves — PrimitiveWrapper's permission checks consult this, see Errors and exceptions).

Library

 * Bundling all four kinds of extension together, rather than exposing four separate registration points on
 * [it.unibo.tuprolog.solve.Solver], is what makes it possible to ship a self-contained feature (e.g. `:io-lib`,
 * `:oop-lib`) as a single pluggable unit. Since more than one [Library] can be loaded into a `Runtime` at once,
 * [alias] is what lets clashing predicate indicators between libraries be resolved without either library needing
 * to know about the other (see [of] and its `alias` parameter).
 *
 * @see Runtime
 * @see it.unibo.tuprolog.solve.libraryOf
 * */
interface Library : Pluggable {

The only addition over Pluggable is alias: String — the name a solver uses to refer to the library, following the pattern \w+(\.\w+)* (segments separated by Library.ALIAS_SEPARATOR, i.e. "."). Aliasing lets a solver disambiguate predicates with clashing signatures across libraries by qualifying them, e.g. prolog.lang:is/2.

Instances are built through the companion factories, not by implementing the interface directly:

  • Library.of(alias, primitives, clauses, operators, functions) — full form (all collections default to empty);
  • Library.of(primitives, clauses, operators, functions) — same, aliased "default";
  • Library.of(alias, library) — re-aliases an existing Library.

CommonBuiltins (see Default predicates) is the running example: an AbstractLibrary object aliased "prolog.lang", whose clauses/primitives/functions delegate to CommonRules/CommonPrimitives/ CommonFunctions respectively. AbstractLibrary and AbstractPluggable supply sensible defaults (empty collections, equals/hashCode/toString based on alias + contents) so a custom library only needs to override what it actually provides. LibraryImpl is the concrete class backing Library.of(...); ExtensionLibrary wraps another Library while overriding only some of its members.

Runtime

A Runtime is an immutable, alias-indexed group of Library instances — what a Solver's libraries property holds (see Solver API):

 * Represents a group of [Library] objects constituting the runtime a logic solver may leverage upon, keyed by
 * [Library.alias] (as the underlying `Map<String, Library>` reflects).
 *
 * A [it.unibo.tuprolog.solve.Solver] loads exactly one [Runtime] (see [it.unibo.tuprolog.solve.SolverFactory.defaultRuntime]);
 * `Runtime` is what lets several independently-authored [Library] instances (e.g. `:io-lib`, `:oop-lib`, a
 * standard-library `Library`) coexist within a single solver, aliasing being the mechanism that resolves clashing
 * predicate indicators without either library needing to know about the other.
 *
 * Construct one via the companion's [empty]/[of] factories, or combine [Library]/[Runtime] instances with [plus].
 *
 * @see Library
 * @see it.unibo.tuprolog.solve.MutableSolver.setRuntime
 */
interface Runtime :
    Pluggable,
    Map<String, Library> {
    /** The [Library.alias] of every library in this runtime; same as this map's [keys]. */
    @JsName("aliases")
    val aliases: Set<String>

    /** All libraries composing this library group */
    @JsName("libraries")
    val libraries: Set<Library>

    /** Merges every library's [Pluggable.clauses] into a single indexed [Theory], using [unificator]. */
    @JsName("asTheory")
    fun asTheory(unificator: Unificator): Theory

    /**
     * Returns a new [Runtime] with [other] added to [libraries].
     * @throws it.unibo.tuprolog.solve.library.exception.AlreadyLoadedLibraryException if a library aliased as [other]'s [Library.alias] is already loaded.
     */
    @JsName("plusLibrary")
    operator fun plus(other: Library): Runtime

Runtime itself implements Pluggable (its operators/clauses/primitives/functions are the union of all contained libraries') and Map<String, Library> (keyed by alias), plus:

  • aliases / libraries — the set of alias strings / Library instances it contains;
  • plus(library) / plus(runtime) — add one or more libraries;
  • minus(library) / minus(alias) / minus(aliases) — remove libraries;
  • update(library) — replace an already-loaded library (same alias) with a new version;
  • asTheory(unificator) — flattens all libraries' clauses into a single Theory.

Build one with Runtime.empty(), Runtime.of(vararg library), or Runtime.of(iterable/sequence).

Loading and unloading

Outside of a running resolution, MutableSolver (see Solver API) exposes loadLibrary(library), unloadLibrary(library) and setRuntime(runtime) directly.

From within a primitive, the same effect is achieved declaratively, via the SideEffect.AlterRuntime family attached to a Solve.ResponseLoadLibrary, UnloadLibraries, UpdateLibrary, AddLibraries, ResetRuntime — since a primitive cannot mutate the ExecutionContext it was given (see Primitives and functions).

Library-related failures are reported as LibraryException (it.unibo.tuprolog.solve.library.exception, extending TuPrologException directly rather than ResolutionException): NoSuchALibraryException and AlreadyLoadedLibraryException.

See Solver design for why libraries — rather than a monolithic built-in set — are the unit of extension for the resolution engine.