moonlight-core 0.1.0.1 → 0.1.0.2
raw patch · 4 files changed
+107/−120 lines, 4 files
Files
- CHANGELOG.md +6/−0
- README.md +4/−114
- moonlight-core.cabal +1/−1
- src-public/Moonlight/Core.hs +96/−5
CHANGELOG.md view
@@ -1,5 +1,11 @@ # Changelog +## 0.1.0.2 - 2026-07-22++- Documentation: the `Moonlight.Core` umbrella header now carries the worked+ recipes — persistent union-find, bounded fixpoints, checked total maps, and+ the matching seam. The README is reduced to a dependency and module map.+ ## 0.1.0.1 - 2026-07-22 - Public named sublibraries drop the redundant package prefix:
README.md view
@@ -3,118 +3,9 @@ > Part of **Moonlight**, the sheaf-theoretic computation layer beneath > [Melusine](https://bluerose.blue) and Pale Meridian. -`moonlight-core` is Moonlight's foundation tier.-It is the total vocabulary every layer above shares: numeric classes, structural-identity, orders, patterns, fixpoints, persistent union-find, finite registries, a-term database, and a host-neutral e-graph program algebra.--## Main idea--Every name reachable from the one import, `Moonlight.Core`, is total. Failure is a-value in the return type: `tryInv` returns `Maybe`, `makeSet` returns-`Either UnionFindAllocationError`, `fixpointBounded` returns-`Either (FixpointDivergence a)`, `mkTotalRegistry` returns `Either [missingKey]`.-Operations with no failure case (`magnitude`, `neg`, `union`) return bare values.--`Moonlight.Core.Unsound` holds the unchecked constructors: importing it by name-asserts a refinement the checker cannot see. Every other constructor reachable-through the umbrella validates at construction.--## Use--```haskell-import Moonlight.Core--reciprocalOfThree :: Maybe Double-reciprocalOfThree = tryInv 3--size :: Double-size = magnitude (neg (2.5 :: Double))-```--## Cookbook--Each recipe shows the main idea in practice: the total path returns a value, the-failure path returns a typed sum.--### Persistent union-find--`makeSet` mints fresh classes through a checked allocation boundary, `union`-merges them, and the structure is persistent; older versions stay valid after a-merge.--```haskell-import Moonlight.Core--partition :: Either UnionFindAllocationError (Bool, Bool)-partition = do- (a, uf1) <- makeSet emptyUnionFind- (b, uf2) <- makeSet uf1- (c, uf3) <- makeSet uf2- let uf = union a b uf3- pure (equivalent a b uf, equivalent a c uf) -- Right (True, False)-```--### Bounded fixpoints--`fixpointBounded` iterates a step to a fixed point under an explicit fuel bound.-Non-convergence returns a typed `Left`.--```haskell-import Moonlight.Core--converge :: Either (FixpointDivergence Int) Int-converge = fixpointBounded 100 (\n -> n `div` 2) 37 -- Right 0--diverges :: Either (FixpointDivergence Int) Int-diverges = fixpointBounded 5 (+1) 0 -- Left (out of fuel)-```--### Checked total maps--`mkTotalRegistry` demands every key of a finite universe; a gap is a typed-`Left [missingKeys]`. In exchange, `lookupTotal` returns a bare value once the-registry is constructed.--```haskell-import Moonlight.Core-import Data.List.NonEmpty (NonEmpty (..))-import qualified Data.Map.Strict as Map--data Slot = Alpha | Beta | Gamma- deriving (Eq, Ord, Show)--instance FiniteUniverse Slot where- finiteUniverse = Alpha :| [Beta, Gamma]--palette :: Either [Slot] (TotalRegistry Slot String)-palette = mkTotalRegistry (Map.fromList [(Alpha, "a"), (Beta, "b"), (Gamma, "c")])--labelOfBeta :: Either [Slot] String-labelOfBeta = fmap (\reg -> lookupTotal reg Beta) palette -- Right "b"--- mkTotalRegistry (Map.fromList [(Alpha, "a")]) == Left [Beta, Gamma]-```--### The matching seam--`ZipMatch` is one-layer structural matching: `zipMatch` succeeds iff two nodes share a-shape, pairing their children positionally. It is the seam the e-graph and rewriting-layers are built on.--```haskell-import Moonlight.Core--data ExprF a = Lit Int | Add a a- deriving (Functor, Foldable, Traversable, Eq, Ord, Show)--instance ZipMatch ExprF where- zipMatch (Lit m) (Lit n) | m == n = Just (Lit m)- zipMatch (Add a1 b1) (Add a2 b2) = Just (Add (a1, a2) (b1, b2))- zipMatch _ _ = Nothing--aligned :: Maybe (ExprF (Int, Int))-aligned = zipMatch (Add 1 2) (Add 10 20) -- Just (Add (1,10) (2,20))-```+`moonlight-core` is Moonlight's foundation tier. The front door is the total+umbrella module `Moonlight.Core`; its header carries the contract, the API map,+and the worked recipes. This page is the dependency map and the build commands. ## Surface & boundaries @@ -127,13 +18,12 @@ | `moonlight-core:moonlight-core-syntax` | `Moonlight.Core.Pattern.AntiUnify` | Patterns and term anti-unification without the full umbrella. | | `moonlight-core:moonlight-core-automata` | `Moonlight.Core.Pattern.Automata` or `.Kernel` | The bottom-up automata substrate and compiled matcher. Depend on syntax too when naming its types directly. | | `moonlight-core:moonlight-core-egraph-program` | `Moonlight.Core.EGraph.Program` | The host-neutral e-graph program algebra without the rest of the foundation. |-| `moonlight-core` | `Moonlight.Core.Unsound` | The explicit trust boundary; see [Main idea](#main-idea). |+| `moonlight-core` | `Moonlight.Core.Unsound` | The explicit trust boundary. | The private implementation slices remain **basis**, **numeric**, **solver**, and **term**. Syntax, automata, and e-graph programs are public because other foundation packages consume those exact owners. The umbrella re-exports the syntax and e-graph-program vocabulary; automata are imported only through the sublibrary.-Haddock carries the signatures; the laws are named and machine-checked in the law suites. ## Test
moonlight-core.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: moonlight-core-version: 0.1.0.1+version: 0.1.0.2 synopsis: Mathematical basis for Pale Meridian. description: A total vocabulary of numeric classes, structural identity, orders, patterns, fixpoints, union-find, finite registries, and host-neutral e-graph programs. license: MIT
src-public/Moonlight/Core.hs view
@@ -1,9 +1,100 @@ {-|-Authoritative frozen umbrella re-export of the moonlight-core foundation-surface. Direct @Moonlight.Core.*@ module imports outside this module are-internal-use-at-own-risk, except for the deliberately explicit-"Moonlight.Core.Unsound" trust boundary. Unsafe refinement constructors are not-re-exported here.+The single, total import of the @moonlight-core@ foundation — the vocabulary+every layer above shares. Every name reachable from @Moonlight.Core@ is+/total/: failure is a value in the return type, never an exception. @tryInv@+returns @Maybe@, @makeSet@ returns @Either UnionFindAllocationError@, and+@fixpointBounded@ returns @Either (FixpointDivergence a)@; operations with no+failure case (@magnitude@, @neg@, @union@) return bare values.++Direct @Moonlight.Core.*@ imports elsewhere are internal-use-at-own-risk, and+the unsafe refinement constructors are not re-exported here. The one deliberate+hole in construction-time checking is "Moonlight.Core.Unsound", whose+constructors assert a refinement the checker cannot see.++The re-exported vocabulary, by role:++* Numeric tower — @Scalar@, @Numeric@ (@OrderedRing@, @OrderedField@,+ @ContinuousField@) and @ApproxEq@, with canonical and exact numbers via+ @Canon@, @CanonicalNumber@, @ExactToken@.+* Validation and refinement — @Validation@, @Refinement@, @Niche@: the+ construction-time checks that back the totality contract.+* Identity and language — @DomainId@, @Identifier@ (with the e-graph+ identifiers and @PatternVar@), @Capability@, @ModuleIdentifier@, and the+ @Language@ / @ZipMatch@ node algebra.+* Theories, patterns and matching — @Theory@, @Pattern@, @Substitution@,+ @Match@: the one-layer structural-matching seam the e-graph and rewriting+ layers stand on.+* Identity hashing — @StableHash@, @Hash@, @DenseKey@.+* Order, finiteness and fixpoints — @Finite@, @Order@, @OrdCollection@,+ @Fix.Order@, and @Fixpoint@ (with the dense solver), which report+ non-convergence as a value.+* Collections and registries — @Aggregate@, @MapAccum@, @MapInvert@, @Queue@,+ @Dedup@, @Scan@, @Relational@, checked-total @TotalRegistry@, and the+ persistent and transactional @UnionFind@.+* Programs and terms — the host-neutral @EGraph.Program@ and @Site.Program@+ algebras and the relational @Term.Database@ with its canonicalization.+* Manifests and metadata — @ProofManifest@, @LawName@, @Guidance@, @Boundary@,+ @Cardinality@, @IsoNorm@, @TypeLevel@, @Error@.++Each module's Haddock carries the signatures; the laws are named through+@LawName@ and machine-checked in the package law suites, not restated here.++Worked recipes follow: the total path returns a value, the failure path a typed+sum. Each assumes @import Moonlight.Core@.++/Persistent union-find./ @makeSet@ mints fresh classes through a checked+allocation boundary, @union@ merges them, and the structure is persistent —+older versions stay valid after a merge.++> partition :: Either UnionFindAllocationError (Bool, Bool)+> partition = do+> (a, uf1) <- makeSet emptyUnionFind+> (b, uf2) <- makeSet uf1+> (c, uf3) <- makeSet uf2+> let uf = union a b uf3+> pure (equivalent a b uf, equivalent a c uf) -- Right (True, False)++/Bounded fixpoints./ @fixpointBounded@ iterates a step to a fixed point under an+explicit fuel bound; non-convergence returns a typed @Left@ rather than looping.++> converge :: Either (FixpointDivergence Int) Int+> converge = fixpointBounded 100 (\n -> n `div` 2) 37 -- Right 0+>+> diverges :: Either (FixpointDivergence Int) Int+> diverges = fixpointBounded 5 (+1) 0 -- Left (out of fuel)++/Checked total maps./ @mkTotalRegistry@ demands every key of a finite universe;+a gap is a typed @Left [missingKeys]@. In exchange @lookupTotal@ returns a bare+value once the registry is constructed (assumes @Data.List.NonEmpty (NonEmpty (..))@+and a qualified @Data.Map.Strict@).++> data Slot = Alpha | Beta | Gamma+> deriving (Eq, Ord, Show)+>+> instance FiniteUniverse Slot where+> finiteUniverse = Alpha :| [Beta, Gamma]+>+> palette :: Either [Slot] (TotalRegistry Slot String)+> palette = mkTotalRegistry (Map.fromList [(Alpha, "a"), (Beta, "b"), (Gamma, "c")])+>+> labelOfBeta :: Either [Slot] String+> labelOfBeta = fmap (\reg -> lookupTotal reg Beta) palette -- Right "b"+> -- mkTotalRegistry (Map.fromList [(Alpha, "a")]) == Left [Beta, Gamma]++/The matching seam./ @ZipMatch@ is one-layer structural matching: @zipMatch@+succeeds iff two nodes share a shape, pairing their children positionally. It is+the seam the e-graph and rewriting layers are built on.++> data ExprF a = Lit Int | Add a a+> deriving (Functor, Foldable, Traversable, Eq, Ord, Show)+>+> instance ZipMatch ExprF where+> zipMatch (Lit m) (Lit n) | m == n = Just (Lit m)+> zipMatch (Add a1 b1) (Add a2 b2) = Just (Add (a1, a2) (b1, b2))+> zipMatch _ _ = Nothing+>+> aligned :: Maybe (ExprF (Int, Int))+> aligned = zipMatch (Add 1 2) (Add 10 20) -- Just (Add (1,10) (2,20)) -} module Moonlight.Core ( -- * Numeric tower