Skip to content

Add experimental era ranges: eon-like spans as type-level lists#1256

Open
carbolymer wants to merge 2 commits into
masterfrom
mgalazyn/support-era-ranges-with-vary
Open

Add experimental era ranges: eon-like spans as type-level lists#1256
carbolymer wants to merge 2 commits into
masterfrom
mgalazyn/support-era-ranges-with-vary

Conversation

@carbolymer

@carbolymer carbolymer commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Context

This PR prototypes era ranges: a generic replacement for the hand-written eon machinery, where an era span is a type-level list sliced from a single timeline instead of a bespoke GADT.

Onwards AlonzoEra         -- Alonzo and everything after it
ShelleyEra :-: BabbageEra -- the closed interval from Shelley to Babbage

The motivation: each eon costs a ~110-150 line module (GADT, Eon/Convert instances, a ~33-item constraint synonym, a CPS reifier, an Is*BasedEra class), adding an era touches all of them, and every constraint bundle except alonzoEraOnwardsConstraints currently ends in a runtime error "TODO Dijkstra".

What this adds, under Cardano.Api.Experimental.Era.Range* (purely additive - no existing eon code is changed):

  • EraIn range era - one generic membership witness replacing per-range GADTs, with an Eon instance so existing combinators (forEraInEon, Featured, ...) work with any range.
  • All/withRange/rangeDict - range constraints are derived by GHC from the per-era instances instead of hand-listed; an era lacking support becomes a compile error at the use site instead of a runtime error.
  • splitRange - total, nestable boundary dispatch replacing the caseShelleyToXOrYEraOnwards family (which errors on Dijkstra and cannot be nested).
  • SomeEraIn range - a vary-based existential (Vary of CardanoEra singletons) with O(1) morph widening between ranges.
  • Compile-time canaries proving the timeline matches the ledger's PreviousEra chain (timelineMatchesLedger) and that SupportedEras tracks the experimental Era GADT (supportedErasAligned).
  • A fix for TestEquality CardanoEra, which was missing the DijkstraEra case (Dijkstra silently tested unequal to itself).

Constraint bundles follow one idiom, with the constraint list written exactly once: a plain alias as the single source of truth (used applied in signatures) plus a two-line class/instance eta-expansion making it passable to withRange unapplied.

Full rationale, trade-offs (including the honest overlap with vary's internals) and migration story are in era-ranges-with-vary-design.md at the repo root.

New dependencies: vary (Hackage, within the pinned index-state) and constraints (already in the build-plan closure).

How to trust this PR

  • The change is additive: apart from the one-line TestEquality fix and cabal wiring, no existing module is touched.

  • Test.Cardano.Api.Experimental.EraRange doubles as the usage showcase: lower bound (ConwayEraTxCert incl. Dijkstra), upper bound (ShelleyEraTxCert, capped at Conway by the class itself), subset narrowing and nested three-way dispatch via splitRange, membership checks (including the TestEquality regression), and a vary round-trip. Run them with:

    cabal run cardano-api:test:cardano-api-test -- -p "EraRange"
    
  • The two canaries make the central claims compile-time checked: if the timeline drifted from the ledger's era chain, or SupportedEras from the experimental Era GADT, the library would not build.

  • Must-NOT-compile counterexamples (requesting ShelleyEraTxCert for a Dijkstra-containing range; an era outside the timeline) are documented next to the tests.

  • Library and tests build warning-free under -Wall -Wredundant-constraints -Wunused-packages; hlint reports no hints.

Checklist

  • Commit sequence broadly makes sense and commits have useful messages
  • New tests are added if needed and existing tests are updated. See Running tests for more details
  • Self-reviewed the diff
  • Changelog fragment added in .changes/

Comment thread cardano-api/src/Cardano/Api/Experimental/Era/Range/Some.hs Fixed
@carbolymer
carbolymer force-pushed the mgalazyn/support-era-ranges-with-vary branch from 5633bf6 to ff0e180 Compare July 16, 2026 15:35
@carbolymer carbolymer changed the title Alternative era ranges Add experimental era ranges: eon-like spans as type-level lists Jul 16, 2026
@carbolymer
carbolymer force-pushed the mgalazyn/support-era-ranges-with-vary branch from ff0e180 to ed7597c Compare July 16, 2026 16:09
Era ranges are type-level lists of era tags, sliced from a single
timeline (ShelleyBasedEras) with From/UpTo/Onwards/(:-:). One generic
witness type `EraIn range era` replaces per-range eon GADTs:

* membership: KnownEras (checkMember, knownEras) and HasEraIn, plus an
  Eon instance so the existing combinators (forEraInEon, Featured, ...)
  work with any range
* constraints: `All c range` is discharged by GHC from the per-era
  instances; withRange/rangeDict bring `c era` into scope from a
  witness, so missing era support is a compile error at the use site
  instead of a runtime "TODO Dijkstra" error
* dispatch: splitRange splits a range at any boundary, totally and
  nestably, replacing the caseShelleyToXOrYEraOnwards pattern
* existentials: SomeEraIn range - a Vary of CardanoEra singletons with
  injectEra/withSomeEra and O(1) morph widening between ranges
* compile-time canaries prove the timeline matches the ledger's
  PreviousEra chain and that SupportedEras tracks the experimental Era
  GADT

Also fixes the missing DijkstraEra arm in TestEquality CardanoEra,
which made Dijkstra silently test unequal to itself.

The test module doubles as a usage showcase: constraint bundles written
once as plain aliases with a two-line class/instance eta-expansion,
requirements stated in leaf signatures, and withRange applied at
enumeration and dispatch boundaries.

New dependencies: vary (Hackage, within the pinned index-state) and
constraints (already in the build-plan closure).
@carbolymer
carbolymer force-pushed the mgalazyn/support-era-ranges-with-vary branch from ed7597c to 221dc78 Compare July 17, 2026 14:51
@carbolymer
carbolymer marked this pull request as ready for review July 17, 2026 15:04
Copilot AI review requested due to automatic review settings July 17, 2026 15:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an experimental “era ranges” abstraction that models era spans as type-level lists sliced from a single timeline, providing generic membership witnesses, derived range-wide constraints, and total/nestable boundary dispatch. The intent is to replace the current hand-written “eon” boilerplate with a smaller generic core while also fixing a correctness bug in TestEquality CardanoEra for DijkstraEra.

Changes:

  • Add Cardano.Api.Experimental.Era.Range (and .Some) implementing EraIn, KnownEras, All/withRange/rangeDict, and total splitRange, plus a vary-based existential SomeEraIn.
  • Add a new test module Test.Cardano.Api.Experimental.EraRange covering lower/upper bounded ranges, subset narrowing via splitRange, nested splits, membership checks, and a vary round-trip.
  • Fix TestEquality CardanoEra by adding the missing DijkstraEra reflexive case; wire new modules and dependencies (vary, constraints) into the cabal file and add a changelog fragment.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
era-ranges-with-vary-design.md Design/rationale document describing the era-range approach and migration story.
cardano-api/src/Cardano/Api/Experimental/Era/Range.hs Public experimental API surface for type-level era ranges and associated operations.
cardano-api/src/Cardano/Api/Experimental/Era/Range/Internal.hs Core implementation: timeline, slicing families, EraIn, KnownEras, All/withRange, and splitRange, plus compile-time canaries.
cardano-api/src/Cardano/Api/Experimental/Era/Range/Some.hs vary-backed existential SomeEraIn utilities (injectEra, withSomeEra, onEra, isEra).
cardano-api/src/Cardano/Api/Era/Internal/Core.hs Bugfix: TestEquality CardanoEra now handles DijkstraEra.
cardano-api/test/cardano-api-test/Test/Cardano/Api/Experimental/EraRange.hs New Hedgehog properties exercising the new range machinery end-to-end.
cardano-api/test/cardano-api-test/cardano-api-test.hs Registers the new EraRange test group.
cardano-api/cardano-api.cabal Exposes the new experimental modules; adds constraints and vary; adds internal module to other-modules; adds test module.
.changes/20260716_cardano_api_experimental_era_ranges.yml Changelog fragment classifying this as feature + bugfix with a detailed description.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants