Liking cljdoc? Tell your friends :D

Changelog

Notable changes to State-space. Earlier releases are described in the GitHub release notes.

0.6.0 (2026-11-11)

The public API was redesigned around three rules: a function has one kind of result, each thing is said in one way, and a mistake fails where it is made. Almost every change below breaks code written for 0.5.0; the table at the end maps old spellings to new ones.

Changed

  • Tracing has its own namespace. state-space.trace offers dfs, postorder, bfs, and best-first with the arguments of the searches in state-space.core, returning events. The :events? option is gone, so the functions in state-space.core always return visits (or, for propagate, progress records).
  • Events are flat. An event is {:event :start|:edge|:enter|:exit ...}. :start and :edge events carry :accepted? and, when rejected, a :reason (:already-visited, :on-path, or :not-better). The nested :phase, the :candidate/:origin/:disposition/:effect vocabulary, and the :expand and :skip phases are gone. A best-first candidate that displaces a pending one reports it under :replaced. Breadth-first duplicates are now reported as rejected edges, like depth-first ones. In depth-first traces, an edge back to a visit that is still being explored has the reason :on-path, which tells a cycle from a repeat. propagate has no event form: its progress records already are its trace.
  • :dedupe chooses the deduplication policy on every search: :search (default, each key once), :path (each key once per route), or :none. It replaces simple-paths, :visit-key nil, and :visit-order. :visit-key is now always a function.
  • postorder is the only way to ask for post-order. dfs no longer accepts :visit-order.
  • best-first no longer treats :priority :distance specially. Without :priority, visits carry :distance and are ordered by it, plus :heuristic when given. With :priority, the search orders by your function and adds nothing. A nil priority throws instead of silently ordering first-in, first-out.
  • propagate takes its join as an argument and no :bottom: (propagate space initial-values join), with options as an optional fourth argument. A state reached for the first time takes the contribution as its value; the join combines later ones.
  • Extensions have shorter names: path, depth, trail, distance.
  • The fold's successor view uses the names of the trace: :transition instead of :via, and :on-path? instead of :back-edge.
  • Arguments are checked when an operation is called. Unknown options, options of another operation, values of the wrong shape, an options argument that is not a map, and starting states that are not a collection throw ExceptionInfo whose data name the :function and the :option or :argument at fault. Previously unused options were ignored. nil uniformly means "use the default".
  • best-first rejects weights its default order cannot handle. A transition whose :weight is negative or not a number throws when the search meets it, as does a :heuristic that does not return a number. A custom :priority is not restricted.
  • Callback results are checked when used. A :decode-edge result without :state, an extension or :descend callback that returns something other than a map or replaces :state, and visit fields that collide with a fold's :value, :transition, or :on-path? throw instead of corrupting the search.

Added

  • :decode-edge accepts a vector of field names for successors that are tuples: [:state :weight] reads [:b 5] as {:state :b :weight 5}.
  • :heuristic on best-first for A* search.
  • :dedupe :path and :none on bfs and best-first: simple paths in order of length, and routes ranked from cheapest to most expensive.
  • Folds under :dedupe :path and :none evaluate each occurrence on its own, without retaining results; under :path, an edge back to the current route arrives with :on-path? true.

Migration

0.5.0Now
ss/path-extension, ss/depth-extension, ss/trail-extension, ss/distance-extensionss/path, ss/depth, ss/trail, ss/distance
(ss/simple-paths space starts opts)(ss/dfs space starts (assoc opts :dedupe :path))
{:visit-key nil}{:dedupe :none}
(ss/dfs ... {:visit-order :postorder})(ss/postorder ...)
(ss/simple-paths ... {:visit-order :postorder})(ss/postorder ... {:dedupe :path})
(ss/dfs ... {:events? true})(trace/dfs ...)
{:event :visit :phase :enter :visit v}{:event :enter :visit v}
{:event :candidate :origin :edge :disposition :discarded ...}{:event :edge :accepted? false ...}
:reason :on-current-path, :already-settled:reason :on-path, :already-visited
:effect :improved-pending:replaced previous-visit
{:decode-edge (fn [[s w]] {:state s :weight w})}{:decode-edge [:state :weight]} (the function still works)
{:extensions [ss/distance-extension] :priority #(+ (:distance %) (h (:state %)))}{:heuristic #(h (:state %))}
(ss/propagate space values {:bottom #{} :join set/union})(ss/propagate space values set/union)
(:back-edge child), (:via child)(:on-path? child), (:transition child)
{:visit-key :path :extensions [ss/path-extension]} to list every route{:dedupe :path}

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close