| Safe Haskell | Safe-Inferred |
|---|---|
| Language | Haskell2010 |
Pattern.Graph
Description
Graph Lens: Interpretive view of Pattern structures as graphs.
This module provides the Graph Lens feature, which enables interpreting Pattern structures as graph structures (nodes, relationships, walks) through a minimal, elegant design based on a single predicate.
Overview
A Graph Lens provides an interpretive view of a Pattern as a graph structure. Rather than defining graph concepts (nodes, relationships, walks) as intrinsic properties of Pattern, they emerge through the lens's interpretation. This design enables multiple graph views of the same Pattern and supports higher-order graphs where relationships or entire graphs become nodes.
Core Design
The Graph Lens consists of:
scopePattern: The Pattern that defines the boundary for all graph operations. Only direct elements of this pattern are considered for graph structure.testNode: A predicate determining which direct elements are nodes. All other graph concepts (relationships, walks) derive from this single predicate.
Design Principles
- **Scope-bounded operations**: All graph operations only consider direct elements
of
scopePattern, never descending into nested structures.
- **Single predicate foundation**: Only
testNodeis required. All other graph predicates (relationships, walks, etc.) are derived from this. - **Context captured at construction**: If a predicate needs context, that context must be captured when the predicate is created, not during evaluation.
- **Interpretation, not intrinsic**: Graph structure is not a property of Pattern itself, but an interpretation through the lens.
Categorical Interpretation
Graph Lens provides a functorial interpretation where Pattern structures are transformed into graph interpretations. The transformation Pattern → Graph interpretation is functorial in nature.
GraphView
GraphView is the universal transformation interface produced from a GraphLens
(via toGraphView) or from a PatternGraph
(via toGraphView). It pairs a snapshot GraphQuery
with a categorized, traversable list of graph elements, enabling lazy pipeline
transformations:
pipeline :: PatternGraph Subject -> PatternGraph Subject pipeline graph = materialize canonicalClassifier LastWriteWins . mapWithContext canonicalClassifier enrich . filterGraph canonicalClassifier isRelevant dissolve . mapAllGraph updateTimestamp . Pattern.PatternGraph.toGraphView canonicalClassifier $ graph
Finalize a GraphView pipeline back to a PatternGraph
by calling materialize.
Example
>>>let graphPattern = pattern "graph" [point "a", point "b", pattern "r1" [point "a", point "b"]]>>>let isAtomic (Pattern _ els) = null els>>>let atomicLens = GraphLens graphPattern isAtomic>>>nodes atomicLens[Pattern "a" [],Pattern "b" []]
See design/graph-lens.md and specs023-graph-lensquickstart.md for
comprehensive examples and usage patterns.
Synopsis
- data GraphLens v = GraphLens {
- scopePattern :: Pattern v
- testNode :: Pattern v -> Bool
- mkGraphLens :: Pattern v -> (Pattern v -> Bool) -> GraphLens v
- nodes :: GraphLens v -> [Pattern v]
- isNode :: GraphLens v -> Pattern v -> Bool
- isRelationship :: GraphLens v -> Pattern v -> Bool
- relationships :: GraphLens v -> [Pattern v]
- source :: GraphLens v -> Pattern v -> Maybe (Pattern v)
- target :: GraphLens v -> Pattern v -> Maybe (Pattern v)
- reverseRel :: Pattern v -> Pattern v
- isWalk :: GraphValue v => GraphLens v -> Pattern v -> Bool
- walks :: GraphValue v => GraphLens v -> [Pattern v]
- walkNodes :: GraphValue v => GraphLens v -> Pattern v -> [Pattern v]
- neighbors :: GraphValue v => GraphLens v -> Pattern v -> [Pattern v]
- incidentRels :: GraphValue v => GraphLens v -> Pattern v -> [Pattern v]
- degree :: GraphValue v => GraphLens v -> Pattern v -> Int
- fromGraphLens :: (GraphValue v, Eq v) => GraphLens v -> GraphQuery v
- toGraphView :: (GraphValue v, Eq v) => GraphClassifier extra v -> GraphLens v -> GraphView extra v
- data GraphView extra v = GraphView {
- viewQuery :: GraphQuery v
- viewElements :: [(GraphClass extra, Pattern v)]
- data Substitution v
Graph Lens Type
A Graph Lens provides an interpretive view of a Pattern as a graph structure.
The lens consists of:
* scopePattern: The Pattern that defines the boundary for all graph operations.
Only direct elements of this pattern are considered for graph structure.
* testNode: A predicate determining which direct elements are nodes.
All other graph concepts (relationships, walks) derive from this predicate.
Categorical Interpretation
Graph Lens provides a functorial interpretation where Pattern structures are transformed into graph interpretations. The transformation Pattern → Graph interpretation is functorial in nature.
Design Principles
- Scope-bounded: All operations only consider direct elements of scopePattern
- Single predicate foundation: Only testNode is required, all else derives
- Context at construction: Predicate context captured when lens is created
- Interpretation, not intrinsic: Graph structure is an interpretation, not a property of Pattern itself
Example
>>>let atomicLens = GraphLens pattern (\(Pattern _ els) -> null els)>>>nodes atomicLens[[a], [b], [c]]
mkGraphLens :: Pattern v -> (Pattern v -> Bool) -> GraphLens v Source #
Construct a GraphLens using a predicate to identify nodes.
This encapsulates the context for interpreting graph structure.
Node Operations
nodes :: GraphLens v -> [Pattern v] Source #
Extract all nodes from the graph lens.
Nodes are direct elements of scopePattern that satisfy the testNode predicate.
Time Complexity
O(n) where n is the number of direct elements in scopePattern
Example
>>>let lens = GraphLens pattern isAtomic>>>nodes lens[[a], [b], [c]]
isNode :: GraphLens v -> Pattern v -> Bool Source #
Determine if a Pattern is a node according to the lens.
This is the context-aware version that uses the lens's testNode predicate. The lens parameter provides the predicate context.
Example
>>>let lens = GraphLens pattern isAtomic>>>isNode lens (point "a")True>>>isNode lens (pattern "rel" [point "a", point "b"])False
Relationship Operations
isRelationship :: GraphLens v -> Pattern v -> Bool Source #
Determine if a Pattern is a relationship according to the lens.
A relationship is a non-node pattern with exactly two node elements.
Properties
- Must not be a node (does not satisfy testNode predicate)
- Must have exactly two elements
- Both elements must be nodes (according to the lens)
Example
>>>let lens = GraphLens pattern isAtomic>>>let rel = pattern "knows" [point "Alice", point "Bob"]>>>isRelationship lens relTrue
relationships :: GraphLens v -> [Pattern v] Source #
Extract all relationships from the graph lens.
Relationships are non-node patterns with exactly two node elements.
Time Complexity
O(n) where n is the number of direct elements in scopePattern
Example
>>>let lens = GraphLens pattern isAtomic>>>relationships lens[[knows | [Alice], [Bob]], [likes | [Bob], [Charlie]]]
source :: GraphLens v -> Pattern v -> Maybe (Pattern v) Source #
Extract the source node from a relationship.
For directed relationships, the source is the first element. Returns Nothing if the pattern is not a relationship.
Example
>>>let lens = GraphLens pattern isAtomic>>>let rel = pattern "knows" [point "Alice", point "Bob"]>>>source lens relJust (point "Alice")
target :: GraphLens v -> Pattern v -> Maybe (Pattern v) Source #
Extract the target node from a relationship.
For directed relationships, the target is the second element. Returns Nothing if the pattern is not a relationship.
Example
>>>let lens = GraphLens pattern isAtomic>>>let rel = pattern "knows" [point "Alice", point "Bob"]>>>target lens relJust (point "Bob")
reverseRel :: Pattern v -> Pattern v Source #
Reverse the direction of a relationship pattern.
Swaps the first and second elements, effectively reversing the relationship direction.
Example
>>>let rel = pattern "knows" [point "Alice", point "Bob"]>>>reverseRel relpattern "knows" [point "Bob", point "Alice"]
Walk Operations
isWalk :: GraphValue v => GraphLens v -> Pattern v -> Bool Source #
Determine if a Pattern is a walk according to the lens.
A walk is a non-node pattern whose elements are all relationships, where consecutive relationships share nodes (target of one equals source of next).
Properties
- Must not be a node (does not satisfy testNode predicate)
- All elements must be relationships (according to the lens)
- Consecutive relationships must be connected
Example
>>>let lens = GraphLens pattern isAtomic>>>let walk = pattern "path" [rel1, rel2, rel3]>>>isWalk lens walkTrue
walks :: GraphValue v => GraphLens v -> [Pattern v] Source #
Extract all walks from the graph lens.
Walks are non-node patterns whose elements are all relationships, where consecutive relationships share nodes.
Time Complexity
O(n) where n is the number of direct elements in scopePattern
Example
>>>let lens = GraphLens pattern isAtomic>>>walks lens[[path | [rel1], [rel2], [rel3]]]
walkNodes :: GraphValue v => GraphLens v -> Pattern v -> [Pattern v] Source #
Extract nodes from a walk in traversal order.
Returns the source of the first relationship, followed by the targets of subsequent relationships. Returns empty list if the pattern is not a valid walk.
Example
>>>let lens = GraphLens pattern isAtomic>>>let walk = pattern "path" [rel1, rel2]>>>walkNodes lens walk[pattern "A", pattern "B", pattern "C"]
Navigation Operations
neighbors :: GraphValue v => GraphLens v -> Pattern v -> [Pattern v] Source #
Find all neighbors of a node.
Neighbors are nodes connected to the given node via relationships (either as source or target).
Time Complexity
O(r) where r is the number of relationships in the graph
Example
>>>let lens = GraphLens pattern isAtomic>>>neighbors lens (point "Alice")[point "Bob", pattern "Charlie"]
incidentRels :: GraphValue v => GraphLens v -> Pattern v -> [Pattern v] Source #
Find all relationships involving a node.
Returns relationships where the node is either source or target.
Time Complexity
O(r) where r is the number of relationships in the graph
Example
>>>let lens = GraphLens pattern isAtomic>>>incidentRels lens (point "Alice")[[knows | [Alice], [Bob]], [likes | [Charlie], [Alice]]]
degree :: GraphValue v => GraphLens v -> Pattern v -> Int Source #
Compute the degree of a node (number of incident relationships).
The degree is the count of relationships where the node is either source or target.
Time Complexity
O(r) where r is the number of relationships in the graph
Example
>>>let lens = GraphLens pattern isAtomic>>>degree lens (point "Alice")3
GraphQuery constructor
fromGraphLens :: (GraphValue v, Eq v) => GraphLens v -> GraphQuery v Source #
Construct a GraphQuery from a GraphLens.
All fields are derived from existing Graph functions.
queryNodeById and queryRelationshipById perform O(n) / O(r) scans
(no index available from GraphLens). queryContainers scans relationships
and walks.
Note: defined here (not in Pattern.Graph.GraphQuery) to avoid a circular import between GraphQuery and Graph.
GraphView constructor
toGraphView :: (GraphValue v, Eq v) => GraphClassifier extra v -> GraphLens v -> GraphView extra v Source #
Construct a GraphView from a GraphLens and a GraphClassifier.
The classifier assigns a GraphClass tag to each element in the lens scope.
viewQuery is the snapshot query built from the same lens.
Note: defined here (not in Pattern.Graph.GraphQuery) to avoid a circular import — GraphQuery cannot import Pattern.Graph.
GraphView and Substitution (re-exported from Pattern.Graph.Types)
data GraphView extra v Source #
A universal interface bridging graph representations to a single queryable and transformable state.
GraphView pairs a GraphQuery (for snapshot traversal) with an
explicitly categorized, traversable list of graph elements. It acts as
a lazy transformation target: transformations operate over viewElements
while algorithms use viewQuery for context lookups.
Construct via toGraphView (from a PatternGraph)
or toGraphView (from a GraphLens). Finalize via
materialize.
Design Principles
viewQueryis the unmodified snapshot — it never reflects in-flight transformations, ensuring deterministic context-aware operations.viewElementsis the mutable list — transformations update this list.- The pairing is shallow: no deep nesting, just a list and a query record.
Constructors
| GraphView | |
Fields
| |
data Substitution v Source #
Defines how to handle gaps in container structures (e.g., walks) when
an internal element is removed by filterGraph.
When a walk's internal relationship is filtered out, the walk becomes
structurally invalid. Substitution dictates the repair strategy.
Design Rationale
There is no one-size-fits-all fallback for container gaps. Sometimes a gap means the entire container is invalid; other times a surrogate placeholder should be sutured in. An explicit parameter forces callers to reason about filtering consequences directly.
Constructors
| DeleteContainer | Remove the entire container (walk, annotation) when any of its required elements are absent after filtering. |
| SpliceGap | Remove the missing element and re-stitch the remaining elements into a shorter container. For a walk, this collapses the gap. |
| ReplaceWithSurrogate (Pattern v) | Substitute the missing element with a fixed surrogate pattern. The surrogate is inserted in place of the removed element. |