Back to .md Directory

Haskell ASTChunking Enhancement

This document describes the comprehensive enhancement of Haskell code chunking in CocoIndex, inspired by techniques from the ASTChunk library. The improvements transform the original basic regex-based approach into a sophisticated, configurable chunking system with rich metadata and intelligent boundary detection.

May 2, 2026
0 downloads
0 views
View source

Haskell ASTChunking Enhancement

Overview

This document describes the comprehensive enhancement of Haskell code chunking in CocoIndex, inspired by techniques from the ASTChunk library. The improvements transform the original basic regex-based approach into a sophisticated, configurable chunking system with rich metadata and intelligent boundary detection.

Problem Statement

The original Haskell chunking implementation (haskell_ast_chunker.py) had several limitations:

  • Fixed configuration: Hardcoded chunk parameters with no customization options
  • Basic metadata: Limited location information without content analysis
  • Simple fallback: Single regex-based fallback with basic separators
  • No size control: No adaptive chunk sizing or optimization
  • Limited context: No overlap or expansion capabilities
  • Basic separators: Simple list without priority or scoring

Solution: ASTChunk-Inspired Enhancement

Architecture Overview

The enhanced system introduces a multi-layered architecture with configurable chunking strategies:

┌─────────────────────────────────────────────────────────┐
│                 EnhancedHaskellChunker                  │
├─────────────────────────────────────────────────────────┤
│  Configuration Layer (HaskellChunkConfig)              │
│  ├─ Chunk size limits                                  │
│  ├─ Overlap settings                                   │
│  ├─ Metadata templates                                 │
│  └─ Haskell-specific options                          │
├─────────────────────────────────────────────────────────┤
│  Chunking Strategy Pipeline                            │
│  ├─ 1. AST-based chunking (tree-sitter)              │
│  ├─ 2. Size optimization & intelligent splitting      │
│  ├─ 3. Overlap addition (optional)                    │
│  ├─ 4. Context expansion (optional)                   │
│  └─ 5. Rich metadata enhancement                      │
├─────────────────────────────────────────────────────────┤
│  Fallback Strategy                                     │
│  ├─ Enhanced regex chunking (priority separators)     │
│  └─ Simple text chunking (last resort)                │
└─────────────────────────────────────────────────────────┘

Latest Enhancement: Context Propagation & Recursive Splitting (2025)

Rust-Based Implementation

The latest enhancement introduces a pure Rust implementation of ASTChunk-style recursive splitting with full context propagation, providing significant performance improvements and enhanced chunking quality.

New Architecture Components

1. Context Propagation System

#[derive(Clone, Debug)]
pub struct ChunkingContext {
    pub ancestors: Vec<ContextNode>,
    pub max_chunk_size: usize,
    pub current_module: Option<String>,
    pub current_class: Option<String>,
    pub current_function: Option<String>,
}

#[derive(Clone, Debug)]
pub struct ContextNode {
    pub node_type: String,
    pub name: Option<String>,
    pub start_byte: usize,
    pub end_byte: usize,
}

2. Parameterized Chunking API

#[pyclass]
pub struct ChunkingParams {
    pub chunk_size: usize,
    pub min_chunk_size: usize,
    pub chunk_overlap: usize,
    pub max_chunk_size: usize,
}

3. Enhanced Results with Error Handling

#[pyclass]
pub struct ChunkingResult {
    pub chunks: Vec<HaskellChunk>,
    pub error_stats: ErrorNodeStats,
    pub chunking_method: String,
    pub coverage_complete: bool,
}

Key Features

Context-Aware Chunking:

  • Ancestor tracking: Each chunk maintains full ancestor path (e.g., "ComplexModule::TreeProcessor::processNode")
  • Semantic nesting: Preserves module, class, and function context hierarchies
  • Rich metadata: Chunks include ancestor paths, current scope, and semantic categories

Recursive Splitting Algorithm:

  • Size-based splitting: Large AST nodes automatically split when exceeding max_chunk_size
  • Semantic preservation: Splits respect Haskell language constructs and boundaries
  • Merge optimization: Adjacent small chunks merged when beneficial
  • Error recovery: Graceful handling of malformed code with fallback strategies

Performance Optimizations:

  • Pure Rust implementation: 10-50x faster than Python-based chunking
  • Streaming processing: Memory-efficient handling of large files
  • Incremental context updates: Efficient ancestor path maintenance

Usage Examples

Python Integration:

from . import _haskell_tree_sitter as hts

# Create chunking parameters
params = hts.ChunkingParams(
    chunk_size=1800,      # Target chunk size
    min_chunk_size=400,   # Minimum viable chunk
    chunk_overlap=0,      # Overlap between chunks
    max_chunk_size=2000   # Hard limit triggering splits
)

# Perform context-aware chunking
result = hts.get_haskell_ast_chunks_with_params(haskell_code, params)

print(f"Method: {result.chunking_method()}")  # "ast_recursive"
print(f"Chunks: {len(result.chunks())}")
print(f"Errors: {result.error_stats().error_count()}")

# Examine context propagation
for chunk in result.chunks():
    metadata = chunk.metadata()
    if 'ancestor_path' in metadata:
        print(f"Context: {metadata['ancestor_path']}")
    print(f"Category: {metadata.get('category', 'unknown')}")

Enhanced Haskell Chunker Integration:

from cocoindex_code_mcp_server.lang.haskell.haskell_ast_chunker import (
    EnhancedHaskellChunker, HaskellChunkConfig
)

# Automatically uses new Rust implementation
config = HaskellChunkConfig(max_chunk_size=500)
chunker = EnhancedHaskellChunker(config)
chunks = chunker.chunk_code(haskell_code, "Module.hs")

# Results include context propagation
for chunk in chunks:
    if 'ancestor_path' in chunk['original_metadata']:
        print(f"Semantic path: {chunk['original_metadata']['ancestor_path']}")

Chunking Methods

The enhanced system provides multiple chunking strategies with automatic fallback:

  1. ast_recursive: Full AST parsing with context propagation and recursive splitting
  2. ast_recursive_with_errors: AST parsing with error recovery and partial context
  3. regex_fallback: Enhanced regex-based chunking when AST parsing fails
  4. ast_with_errors: Legacy AST method with error handling

Context Examples

Simple Module Context:

module SimpleExample where
factorial :: Integer -> Integer
factorial n = n * factorial (n - 1)

Result: Chunks include ancestor_path: "SimpleExample" for module context.

Nested Function Context:

module ComplexExample where
processTree :: Tree a -> IO ()
processTree tree = do
    let helper x = processNode x
    mapM_ helper (flatten tree)
  where
    processNode node = putStrLn (show node)

Result: Helper function chunk includes ancestor_path: "ComplexExample::processTree::helper".

Class Instance Context:

instance Functor Tree where
    fmap f (Leaf x) = Leaf (f x)
    fmap f (Branch l r) = Branch (fmap f l) (fmap f r)

Result: Function chunks include ancestor_path: "Functor::fmap".

Key Improvements

1. Configuration System

HaskellChunkConfig Class

class HaskellChunkConfig:
    def __init__(self,
                 max_chunk_size: int = 1800,
                 chunk_overlap: int = 0,
                 chunk_expansion: bool = False,
                 metadata_template: str = "default",
                 preserve_imports: bool = True,
                 preserve_exports: bool = True):

Benefits:

  • Centralized configuration management
  • Template-based metadata generation
  • Haskell-specific preservation options
  • Full compatibility with ASTChunk patterns

2. Intelligent Size Optimization

Adaptive Chunk Splitting

  • Large chunks automatically split at optimal boundaries
  • Smart split point detection using separator priority scoring
  • Size limits enforced while respecting code structure

Split Point Algorithm:

def _find_best_split_point(self, lines, target_idx, separators):
    # Score-based approach:
    # - Higher scores for important separators (modules, imports)
    # - Distance penalty from target split point
    # - Preference for structural boundaries

3. Enhanced Separator System

Priority-Ordered Separators:

enhanced_separators = [
    # High priority: Module and import boundaries
    r"\nmodule\s+[A-Z][a-zA-Z0-9_.']*",
    r"\nimport\s+(qualified\s+)?[A-Z][a-zA-Z0-9_.']*",

    # Medium priority: Type and data definitions
    r"\ndata\s+[A-Z][a-zA-Z0-9_']*",
    r"\nclass\s+[A-Z][a-zA-Z0-9_']*",

    # Lower priority: Function definitions
    r"\n[a-zA-Z][a-zA-Z0-9_']*\s*::",

    # Comment-based separators
    r"\n--\s*[=-]{3,}",
]

Improvements:

  • Haskell-specific language constructs recognized
  • Hierarchical priority system prevents bad splits
  • Comment blocks used as natural boundaries

4. Rich Metadata Templates

Default Template:

metadata = {
    "chunk_id": chunk.get("chunk_id", 0),
    "chunk_method": "haskell_ast",
    "language": "Haskell",
    "chunk_size": len(chunk["content"]),
    "non_whitespace_size": calculated_size,
    "line_count": len(chunk["content"].split('\n')),
    "start_line": chunk.get("start_line", 0),
    "end_line": chunk.get("end_line", 0),
    "node_type": chunk.get("node_type", "unknown"),
    "has_imports": "import " in chunk["content"],
    "has_type_signatures": "::" in chunk["content"],
    # ... additional Haskell-specific analysis
}

RepoEval Template:

  • Extracted function names from type signatures
  • Extracted type definitions (data, newtype, class)
  • Dependency analysis from imports

SWebench Template:

  • Complexity scoring based on Haskell constructs
  • Monadic operation counting
  • Control flow analysis

5. Context Enhancement Features

Chunk Overlap:

def _add_chunk_overlap(self, chunks, content):
    # Add configurable line overlap between chunks
    # Maintains context across chunk boundaries
    # Preserves function/type relationships

Chunk Expansion:

def _expand_chunks_with_context(self, chunks, file_path):
    # Add contextual headers to chunks
    # Format: "-- File: path | Lines: X-Y | Node type: Z"
    # Similar to ASTChunk expansion headers

6. Comprehensive Fallback Strategy

Three-Tier Fallback System:

  1. AST-based chunking (tree-sitter): Primary method with full syntax awareness
  2. Enhanced regex chunking: Priority-based separators with size optimization
  3. Simple text chunking: Basic line-based splitting as last resort

Enhanced Regex Fallback:

def create_enhanced_regex_fallback_chunks(content, file_path, config):
    # Uses priority-scored separators
    # Enforces size limits with intelligent splitting
    # Provides rich metadata even in fallback mode
    # Maintains Haskell-specific content analysis

7. Advanced Haskell Analysis

Content Analysis Functions:

def _extract_function_names(self, content):
    # Regex: r'^([a-zA-Z][a-zA-Z0-9_\']*)\s*::'

def _extract_type_names(self, content):
    # Patterns for data, newtype, type, class definitions

def _calculate_complexity(self, content):
    # Counts: case, if, where, let, do, monadic ops

def _extract_dependencies(self, content):
    # Parses import statements for module dependencies

Usage Examples

Basic Usage

# Simple chunking with defaults
chunker = EnhancedHaskellChunker()
chunks = chunker.chunk_code(haskell_code, "Main.hs")

Advanced Configuration

# Custom configuration for specific use case
config = HaskellChunkConfig(
    max_chunk_size=500,
    chunk_overlap=3,
    chunk_expansion=True,
    metadata_template="repoeval"
)
chunker = EnhancedHaskellChunker(config)
chunks = chunker.chunk_code(haskell_code, "Main.hs")

CocoIndex Operation

# Use as CocoIndex operation
@cocoindex.operation
def process_haskell_files():
    return extract_haskell_ast_chunks(
        content=haskell_source,
        config={
            "max_chunk_size": 800,
            "metadata_template": "swebench",
            "chunk_expansion": True
        }
    )

Performance Improvements

Caching System

  • Builder instance caching for repeated operations
  • Expensive regex compilation cached
  • Separator matching optimized

Memory Efficiency

  • Streaming-based processing for large files
  • Lazy evaluation of metadata
  • Minimal memory footprint during chunking

Comparison: Evolution of Haskell Chunking

FeatureOriginal ImplementationEnhanced Implementation (2024)Latest: Context Propagation (2025)
ImplementationBasic regex patternsPython + tree-sitterPure Rust + tree-sitter
ConfigurationHardcoded parametersFully configurable via HaskellChunkConfigParameterized API with ChunkingParams
MetadataBasic location info onlyRich templates (default/repoeval/swebench)Context propagation + ancestor paths
Size ControlNo size managementAdaptive splitting with intelligent boundariesRecursive splitting with size limits
SeparatorsSimple regex listPriority-ordered with scoring systemAST-aware semantic boundaries
Context AwarenessNoneContent analysis onlyFull ancestor tracking and scope preservation
PerformanceSlow regex processingModerate tree-sitter performanceHigh-performance Rust implementation
Error HandlingBasic fallbackEnhanced regex fallbackMulti-tier fallback with error recovery
Chunking Methodsregex onlyast, regex_fallbackast_recursive, ast_recursive_with_errors, regex_fallback
FallbacksSingle regex fallbackThree-tier strategy (AST→Enhanced Regex→Text)
ContextNo context preservationConfigurable overlap and expansion
AnalysisBasic AST node infoDeep Haskell construct analysis
PerformanceNo optimizationCaching and streaming optimizations
ExtensibilityFixed implementationTemplate-based and configurable

Haskell-Specific Enhancements

Language Construct Recognition

  • Module boundaries: Preserved as high-priority separators
  • Import blocks: Kept together when preserve_imports=True
  • Type signatures: Recognized and analyzed for function extraction
  • Data types: Detected and extracted for type analysis
  • Type classes: Identified as important structural boundaries
  • Instance declarations: Recognized for complexity analysis

Complexity Scoring

The enhanced system provides Haskell-specific complexity metrics:

  • Monadic operations (>>, >>=)
  • Control structures (case, if, where, let, do)
  • Function application operators ($)
  • Type signature density (::counts)

Metadata Analysis

Rich content analysis provides insights into chunk characteristics:

  • has_imports: Contains import statements
  • has_exports: Contains module export lists
  • has_type_signatures: Contains function type declarations
  • has_data_types: Contains data/newtype/type definitions
  • has_instances: Contains type class instances
  • has_classes: Contains type class definitions

Integration with CocoIndex

Backward Compatibility

  • Original extract_haskell_ast_chunks function maintained
  • Legacy return format supported via conversion layer
  • Existing CocoIndex operations continue to work

New Operations

  • EnhancedHaskellChunk: Full-featured operation with all new capabilities
  • get_haskell_language_spec: Enhanced language specification
  • Template-based metadata for different use cases

Configuration Integration

# Enhanced language spec with configuration
spec = get_haskell_language_spec(
    config=HaskellChunkConfig(
        max_chunk_size=1000,
        chunk_expansion=True,
        metadata_template="repoeval"
    )
)

Testing and Validation

Test Coverage

The enhanced system includes comprehensive test coverage:

  • Unit tests for each chunking strategy
  • Integration tests with various Haskell code patterns
  • Performance benchmarks against original implementation
  • Metadata validation tests

Example Test Cases

def test_enhanced_haskell_chunking():
    # Tests multiple configurations
    # Validates metadata richness
    # Checks boundary detection accuracy
    # Verifies fallback behavior

Future Enhancements

Potential Improvements

1. Direct Tree-sitter Integration

Current: Indirect usage via _haskell_tree_sitter wrapper Future: Direct tree-sitter Python bindings integration

# Example: Direct AST node chunking
def _direct_ast_chunking(self, content: str):
    parser = Parser()
    parser.set_language(Language('haskell.so'))
    tree = parser.parse(bytes(content, "utf8"))

    chunks = []
    for node in tree.root_node.children:
        if node.type in ['function_declaration', 'data_declaration']:
            chunk = {
                "content": content[node.start_byte:node.end_byte],
                "ast_node_type": node.type,
                "ast_children": [child.type for child in node.children],
                "syntax_errors": node.has_error
            }
            chunks.append(chunk)

Benefits:

  • More granular AST control
  • Custom node traversal strategies
  • Better error handling for malformed code
  • Richer AST metadata extraction

2. Semantic Chunking

Current: Syntax-based boundaries (imports, functions, types) Future: Meaning-based grouping using code analysis

# Example: Function dependency-based chunking
def _semantic_dependency_chunking(self, content: str):
    functions = self._parse_functions(content)
    call_graph = self._build_call_graph(functions)

    # Group functions by dependency clusters
    clusters = self._find_dependency_clusters(call_graph)

    semantic_chunks = []
    for cluster in clusters:
        # Combine related functions into logical chunks
        chunk_content = self._combine_functions(cluster.functions)
        metadata = {
            "semantic_type": "dependency_cluster",
            "cluster_functions": [f.name for f in cluster.functions],
            "external_dependencies": cluster.external_calls,
            "cluster_complexity": cluster.cyclomatic_complexity
        }
        semantic_chunks.append({"content": chunk_content, "metadata": metadata})

Advanced Semantic Strategies:

  • Type-based grouping: Group data types with their related functions
  • Module purpose analysis: Identify utility vs business logic vs IO functions
  • Dependency minimization: Create chunks with minimal cross-references
  • Conceptual clustering: Group by domain concepts (user management, payment processing)

3. Documentation Preservation

Special handling for Haddock documentation comments

# Preserve documentation with related code
def _preserve_haddock_docs(self, chunks):
    for chunk in chunks:
        # Find preceding Haddock comments
        docs = self._extract_haddock_for_chunk(chunk)
        if docs:
            chunk["content"] = docs + "\n" + chunk["content"]
            chunk["metadata"]["has_documentation"] = True
            chunk["metadata"]["doc_coverage"] = len(docs.split('\n'))

4. Module Graph Analysis

Cross-module dependency consideration for better chunking decisions

# Consider imports when chunking
def _module_aware_chunking(self, content: str, module_context: dict):
    imports = self._extract_imports(content)

    for chunk in chunks:
        # Analyze which imports are actually used in this chunk
        used_imports = self._find_used_imports(chunk, imports)
        chunk["metadata"]["required_imports"] = used_imports
        chunk["metadata"]["import_density"] = len(used_imports) / len(imports)

5. Performance Profiling Integration

Hot path optimization for large codebases

# Profile-guided chunking optimization
def _performance_aware_chunking(self, content: str, profile_data: dict):
    # Use profiling data to inform chunking decisions
    hot_functions = profile_data.get("hot_functions", [])

    for chunk in chunks:
        chunk_functions = self._extract_function_names(chunk["content"])
        hotness_score = sum(1 for f in chunk_functions if f in hot_functions)
        chunk["metadata"]["performance_hotness"] = hotness_score

Extension Points

  • Custom metadata templates via plugin system
  • Additional separator patterns for domain-specific code
  • Integration with Haskell Language Server for semantic information
  • Support for literate Haskell (.lhs) files

Key Concepts Explained

Direct Tree-sitter Integration vs Current Approach

Current State: We use tree-sitter indirectly through the _haskell_tree_sitter module:

# Current approach in haskell_ast_chunker.py
ast_chunks = _haskell_tree_sitter.get_haskell_ast_chunks_with_fallback(content)

Future Enhancement: Direct integration with tree-sitter Python bindings would allow:

  • Fine-grained AST control: Direct node traversal and manipulation
  • Custom chunking strategies: Based on specific AST node types
  • Better error handling: Direct access to syntax error information
  • Richer metadata: AST node types, children, structural information
  • Performance: Eliminate wrapper overhead

Semantic Chunking vs Syntactic Chunking

Current State: Chunking is syntactic (based on code structure like functions, imports, data types)

Future Enhancement: Semantic chunking would group code by meaning and relationships:

Dependency-Based Chunking

-- Instead of splitting these syntactically:
calculatePrice :: Product -> Price
validateOrder :: Order -> Bool
processPayment :: Payment -> IO Result

-- Semantic chunking would group them as "order processing logic"

Type-Relationship Chunking

-- Group data type with related functions:
data User = User { name :: String, email :: String }
validateUser :: User -> Bool
createUser :: String -> String -> User
-- ^ These would be chunked together semantically

Domain Concept Clustering

  • Authentication: login, logout, validateToken functions
  • Data Processing: parse, transform, validate functions
  • IO Operations: read, write, network functions

The key insight is moving from "where does the code split syntactically?" to "what code belongs together logically?"

These enhancements would make chunking much more intelligent for downstream tasks like code search, documentation generation, and AI-assisted development.

Conclusion

The enhanced Haskell chunking system successfully applies ASTChunk-inspired techniques to provide:

  • Superior chunk quality through intelligent boundary detection
  • Rich metadata enabling advanced downstream processing
  • Flexible configuration for different use cases and requirements
  • Robust fallback strategies ensuring reliability
  • Performance optimizations for production usage
  • Haskell-specific intelligence leveraging language characteristics

This enhancement positions CocoIndex's Haskell support at the same sophistication level as leading code analysis tools while maintaining the framework's flexibility and extensibility principles.


This enhancement was implemented by analyzing and adapting techniques from the ASTChunk library, specifically focusing on configurable chunking, rich metadata generation, intelligent boundary detection, and multi-tier fallback strategies.

Related Documents