Skip to content

Commit 5c91e37

Browse files
authored
Merge pull request #67 from relateby/039-representation-map
039 representation map
2 parents ea2a4c2 + c94adbd commit 5c91e37

18 files changed

Lines changed: 2366 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ Auto-generated from all feature plans. Last updated: 2026-01-23
55
## Active Technologies
66
- Haskell (GHC 9.12.2) + `Data.Map.Strict`, `Data.List (foldl')`, `Data.Maybe (mapMaybe)` — all already imported in Transform.hs (037-topo-shape-sort)
77
- N/A — pure in-memory algorithm (037-topo-shape-sort)
8+
- Haskell (GHC 9.12.2) + base, containers — no new dependencies required (039-representation-map)
9+
- N/A — pure in-memory types (039-representation-map)
810

911
- Haskell (GHC 9.12.2) (031-pattern-reconciliation)
1012

@@ -24,6 +26,7 @@ tests/
2426
Haskell (GHC 9.12.2): Follow standard conventions
2527

2628
## Recent Changes
29+
- 039-representation-map: Added Haskell (GHC 9.12.2) + base, containers — no new dependencies required
2730
- 037-topo-shape-sort: Added Haskell (GHC 9.12.2) + `Data.Map.Strict`, `Data.List (foldl')`, `Data.Maybe (mapMaybe)` — all already imported in Transform.hs
2831

2932
- 031-pattern-reconciliation: Added Haskell (GHC 9.12.2)

cabal.project

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@ packages:
88
libs/gram/
99
apps/gramref-cli/
1010

11+
package pattern
12+
tests: True
13+
1114
-- Use GHC 9.12.2 (latest stable version with good tooling support)
1215
with-compiler: ghc-9.12.2

libs/pattern/pattern.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ library
2323
exposed-modules:
2424
Pattern
2525
Pattern.Core
26+
Pattern.RepresentationMap
2627
Pattern.Graph
2728
Pattern.Graph.GraphClassifier
2829
Pattern.Graph.GraphQuery
@@ -59,6 +60,7 @@ test-suite pattern-test
5960
Spec.Pattern.PatternGraphProperties
6061
Spec.Pattern.PatternGraphSpec
6162
Spec.Pattern.Properties
63+
Spec.Pattern.RepresentationMapSpec
6264
Spec.Pattern.ReconcileSpec
6365
Spec.Pattern.ReconcileProperties
6466

libs/pattern/src/Pattern.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@
7070
module Pattern
7171
( -- * Core Pattern Type and Operations
7272
module Pattern.Core
73+
-- * Representation Maps
74+
, module Pattern.RepresentationMap
7375
-- * Graph Operations and GraphView
7476
, module Pattern.Graph
7577
-- * Portable Graph Query Interface
@@ -89,6 +91,7 @@ module Pattern
8991
) where
9092

9193
import Pattern.Core
94+
import Pattern.RepresentationMap
9295
import Pattern.Graph
9396
import Pattern.Graph.GraphQuery
9497
import Pattern.Graph.Transform

libs/pattern/src/Pattern/Core.hs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE RankNTypes #-}
12
{-# LANGUAGE InstanceSigs #-}
23
{-# LANGUAGE MultiParamTypeClasses #-}
34
{-# LANGUAGE FlexibleInstances #-}
@@ -32,6 +33,9 @@ module Pattern.Core
3233
, depthAt
3334
, sizeAt
3435
, indicesAt
36+
-- * Shape Kinds
37+
, PatternKind(..)
38+
, checkKind
3539
-- * Paramorphism Functions
3640
, ScopeQuery(..)
3741
, TrivialScope
@@ -1187,6 +1191,38 @@ class ScopeQuery q v where
11871191
-- | Enumerate every element in scope.
11881192
allElements :: q v -> [Pattern v]
11891193

1194+
-- | A named, scope-aware description of a family of pattern shapes.
1195+
--
1196+
-- A 'PatternKind' acts like a subobject classifier for 'Pattern': it names a
1197+
-- recognizable subtype via a predicate rather than by introducing a new data
1198+
-- type. The canonical example witnesses that the kind is inhabited.
1199+
--
1200+
-- Invariant:
1201+
--
1202+
-- * For any valid scope @q@, @kindPred k q (kindExample k) == True@.
1203+
data PatternKind v = PatternKind
1204+
{ kindName :: String
1205+
-- ^ Unique human-readable name for the kind.
1206+
-- Used for diagnostics and runtime compatibility checks during map composition.
1207+
, kindPred :: forall q. ScopeQuery q v => q v -> Pattern v -> Bool
1208+
-- ^ Membership predicate for the kind.
1209+
-- Structural kinds may ignore the scope argument; scope-relative kinds can
1210+
-- inspect 'containers', 'siblings', 'byIdentity', or 'allElements'.
1211+
, kindExample :: Pattern v
1212+
-- ^ Canonical example inhabiting the kind.
1213+
-- Useful for smoke tests, documentation, and generator seeds.
1214+
}
1215+
1216+
-- | Check whether a pattern belongs to a named kind within a scope.
1217+
--
1218+
-- This is the direct convenience wrapper over 'kindPred'.
1219+
--
1220+
-- Categorical note: 'kindPred' is the characteristic morphism for the
1221+
-- subobject named by the 'PatternKind'; 'checkKind' applies that morphism at a
1222+
-- concrete scope and pattern.
1223+
checkKind :: ScopeQuery q v => PatternKind v -> q v -> Pattern v -> Bool
1224+
checkKind k q p = kindPred k q p
1225+
11901226
-- | The subtree-only scope used by 'para'.
11911227
--
11921228
-- This is the smallest useful scope provider: it fixes visibility to one rooted
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{-# LANGUAGE RankNTypes #-}
2+
-- | Invertible mappings between named pattern kinds.
3+
--
4+
-- A 'RepresentationMap' names a pair of compatible structural transforms between
5+
-- two 'PatternKind's. The map itself carries only executable behavior and its
6+
-- round-trip witness. Explanatory metadata about how a map works is deferred for
7+
-- a later design that can express declarative, checkable claims rather than
8+
-- inline prose attached to the value.
9+
module Pattern.RepresentationMap
10+
( RepresentationMap(..)
11+
, compose
12+
)
13+
where
14+
15+
import Pattern.Core (Pattern, PatternKind, ScopeQuery, kindName)
16+
17+
-- | A named, invertible mapping between two pattern kinds.
18+
--
19+
-- A 'RepresentationMap' is the executable form of an isomorphism between named
20+
-- shapes. 'forward' and 'inverse' are the two morphism components, while
21+
-- 'roundTrip' is the machine-checkable witness that the mapping preserves
22+
-- information on the domain kind.
23+
--
24+
-- Invariants for domain-kind inputs:
25+
--
26+
-- * 'forward' should produce a pattern accepted by 'codomain'.
27+
-- * 'roundTrip' should hold.
28+
-- * When 'roundTrip' holds, @(inverse m q . forward m q) p == p@ structurally.
29+
--
30+
-- Notes:
31+
--
32+
-- * Scope remains polymorphic, so maps are not tied to a particular backing
33+
-- representation.
34+
-- * Declarative, machine-checkable claims about map-specific encoding choices
35+
-- are deferred; for now those details live in documentation and tests next to
36+
-- concrete maps.
37+
data RepresentationMap v = RepresentationMap
38+
{ repMapName :: String
39+
-- ^ Unique human-readable name for the map.
40+
, repMapDomain :: PatternKind v
41+
-- ^ Source kind. 'forward' is intended for patterns of this kind.
42+
, repMapCodomain :: PatternKind v
43+
-- ^ Target kind. 'forward' should produce patterns of this kind.
44+
, repMapForward :: forall q. ScopeQuery q v => q v -> Pattern v -> Pattern v
45+
-- ^ Domain-to-codomain transform, polymorphic over any valid scope.
46+
, repMapInverse :: forall q. ScopeQuery q v => q v -> Pattern v -> Pattern v
47+
-- ^ Codomain-to-domain transform, polymorphic over any valid scope.
48+
, repMapRoundTrip :: forall q. ScopeQuery q v => q v -> Pattern v -> Bool
49+
-- ^ Isomorphism witness for domain-kind inputs at a given scope.
50+
}
51+
52+
-- | Compose two compatible representation maps.
53+
--
54+
-- Composition is defined only when the codomain kind of the first map has the
55+
-- same 'kindName' as the domain kind of the second. The resulting map keeps
56+
-- the first domain, the second codomain, composes 'forward' left-to-right,
57+
-- composes 'inverse' right-to-left, and preserves round-trip validation in the
58+
-- same order.
59+
--
60+
-- Categorical note: this is morphism composition for the category whose
61+
-- objects are 'PatternKind's and whose isomorphisms are 'RepresentationMap's.
62+
compose :: RepresentationMap v -> RepresentationMap v -> Either String (RepresentationMap v)
63+
compose m1 m2
64+
| kindName (repMapCodomain m1) /= kindName (repMapDomain m2) =
65+
Left $
66+
"compose: codomain of '" <> repMapName m1
67+
<> "' (" <> kindName (repMapCodomain m1)
68+
<> ") does not match domain of '" <> repMapName m2
69+
<> "' (" <> kindName (repMapDomain m2) <> ")"
70+
| otherwise =
71+
Right
72+
RepresentationMap
73+
{ repMapName = repMapName m1 <> " >>> " <> repMapName m2
74+
, repMapDomain = repMapDomain m1
75+
, repMapCodomain = repMapCodomain m2
76+
, repMapForward = \q p -> repMapForward m2 q (repMapForward m1 q p)
77+
, repMapInverse = \q p -> repMapInverse m1 q (repMapInverse m2 q p)
78+
, repMapRoundTrip =
79+
\q p -> repMapRoundTrip m1 q p && repMapRoundTrip m2 q (repMapForward m1 q p)
80+
}

0 commit comments

Comments
 (0)