packages feed

scxml-statecharts-0.2.0.0: CHANGELOG.md

# Changelog

## 0.2.0.0 -- 2026-09-17

- **Breaking.** The `event` attribute now names one event and then the Haskell
  types its constructor holds, so `event="Order Items Int"` declares
  `Order Items Int`. SCXML reads the attribute as a space-separated list of
  event descriptors, and that shorthand is gone: `event="A B"` no longer means
  two transitions with one target, which is written as two `<transition>`
  elements instead. This is the one place the parser knowingly differs from
  the specification.
- The event reaching a callback is now the one the caller passed in rather
  than a value rebuilt from its name, so whatever payload it carries survives
  the trip, and an event a callback raises carries its own. The evaluator is
  parameterised over the event type and still selects transitions by name
  alone, so a payload never decides where the chart goes; that stays with the
  events a callback raises. `Def` trades `defEventFromName :: Text -> Maybe ev`
  for a total `defDoneEvent :: StateId -> ev`, since `done.state` events are
  the only ones the evaluator synthesises and they carry nothing.
- A payload type is one type constructor, optionally module-qualified,
  resolved after the quasiquote like a callback name, so it may be defined
  below it. `Maybe Int`, `[Int]` and tuples cannot be told apart from separate
  fields in an attribute whose parts are separated by spaces, so they go
  through a type alias; writing one directly is rejected with a message naming
  the way in. Allowing them later is a compatible change.
- Every transition naming an event must declare the same payload for it, since
  they all reach the one generated constructor, and a disagreement is a
  compile error naming both places.
- **Breaking.** An event that carries data costs `FsmEvent` its derived `Ord`,
  `Enum` and `Bounded`: `Ord` would demand an instance of every payload type,
  and the other two need every constructor nullary. A chart whose events carry
  nothing derives all of them as before.
- The Nix dev shell gained zlib, which `xml-conduit` reaches through
  `conduit-extra` and `streaming-commons`. Without it `cabal build` compiled
  everything and then failed at the link with `cannot find -lz`, and every
  Template Haskell splice warned about `libz.so`.

## 0.1.0.0 -- 2026-09-09

First release.

- `scxml` declaration quasiquoter generating `FsmState`, `FsmEvent`,
  `fsmChart`, `initiateStateMachine` and `notifyStateMachine`.
- Hierarchy (compound states as sum types), parallel regions (as products),
  `<onentry>` and `<onexit>` callbacks named in the XML, entry callbacks that
  raise events, and SCXML `done.state.X` completion events.
- Unmatched events leave the state unchanged, as in SCXML.
- Compile-time validation: strict XML (via `xml-conduit`), state ids, event
  names, callback names, a level rule requiring a transition to target a
  sibling of its source, and a rule forbidding transitions on a region of a
  `<parallel>`. `initial` is required on every compound state and must name a
  direct child; SCXML's "first child in document order" default is not
  supported.
- The evaluator is a single recursive pass over the tree. Because a transition
  may only target a sibling, a state only ever rearranges its own children, so
  there are no least common ancestors, exit-set filters over the whole
  configuration, or conflicts between transitions at different depths. The
  derived index is gone, along with lookup by id, parent links and document
  order. Innermost-wins is now explicit: children are asked first and a state
  acts only if nothing below it did.
- A `<final>` state is rejected as a direct region of a `<parallel>`. SCXML
  does not allow it, and it used to report the whole parallel complete before
  the other regions had run.
- The chart is a tree: a state owns its children as `Node`s, and a compound
  state keeps its initial child first, so a dangling child, a disagreeing
  parent and an initial child that is not one of its own are all
  unrepresentable. Parent and document order are derived into an index rather
  than stored. A compound state's initial child is therefore its first
  generated constructor, which changes derived `Ord` for charts that do not
  write the initial state first.
  Only the interpreter needs the derived index; the parser and the generator
  walk the tree, so a transition target is checked against the source's
  siblings rather than by looking up parents.
- `done.state.X` may only be handled on `X` itself, so completion climbs one
  level at a time through `<final>` states and validation is entirely local to
  a state and its neighbours.
- A transition on an enclosing state acts as a default that an inner state can
  override, since the innermost matching transition wins and only it is taken.
  Documented rather than warned about: a Template Haskell warning becomes an
  error under `-Werror` and cannot be exempted by flag.
- The public API is the `scxml` quasiquoter alone. `serializeStateMachine` and
  `deserializeStateMachine` are generated into the calling module, so a chart
  needs no other import. The remaining modules are not exposed.
- Transitions are a map from event name to target, so two transitions on one
  state for the same event are unrepresentable, and document order never
  decides which transition is taken. A transition naming more than one target
  is rejected; both previously compiled and silently produced a wrong state.
- `Kind` carries each state's children, so an atomic or final state with
  children, a compound state without an initial child, and a `<parallel>`
  without regions are all unrepresentable rather than merely rejected.
- `Show`/`Read` on every generated type, and `toStateIds`/`fromStateIds` for
  storing a state outside Haskell as the SCXML configuration.