SLOP Modules
Composition Without Collision
1. The Problem
When you copy code from different sources:
# From library A
def process(item):
return item.upper()
# From library B - COLLISION
def process(item):
return item.strip()
# Which one is called?
result = process(x) # Ambiguous!
Worse - hidden dependencies:
# Original source file had:
# ===SOURCE: utils===
# def clean(x): return x.strip()
#
# ===SOURCE: processor===
# def process(x): return utils.clean(x)
# You copy just processor, but you have a DIFFERENT utils:
def clean(x):
return x * 2 # Totally different!
# processor silently uses wrong utils
2. The Solution
2.1 Explicit Dependencies
Every reusable block declares what it needs:
===SOURCE: processor===
id: "mycompany/processor@v1"
uses: {utils: "mycompany/utils@v1"} # I need THIS utils
---
def process(item):
return utils.clean(item)
2.2 Wiring at the Edge
Dependencies are resolved at import time, not inside the block:
===USE: mycompany/processor===
===USE: mycompany/utils===
# Auto-wired: processor.utils → mycompany/utils (IDs match)
===MAIN===
result = processor.process(data)
Or with explicit remapping:
===USE: mycompany/processor with {utils: other/utils}===
===USE: other/utils===
# Explicit: processor.utils → other/utils
===MAIN===
result = processor.process(data)
3. Block Format
3.1 Full Header
===SOURCE: human_readable_name===
id: "namespace/name@version"
uses: {
local_name: "required/id@version",
other: "other/id@version"
}
provides: [func1, func2] # Optional: default exports all
---
# Code here
def func1():
pass
def func2():
other.helper() # Uses declared dependency
3.2 Self-Contained Block
No dependencies = copy anywhere:
===SOURCE: string_helpers===
id: "lib/string_helpers@v1"
uses: {} # Self-contained!
---
def clean(s):
return s.strip().lower()
def normalize(s):
return " ".join(s.split())
3.3 One-Shot Script (No Headers)
For LLM-generated scripts, no boilerplate needed:
# Just write code - no headers required
contacts = salesforce.query("SELECT * FROM Contact")
for contact in contacts with rate(10/s):
contact.company = clearbit.lookup(contact.email).company
emit(count: len(contacts))
4. Dependency Resolution
4.1 Auto-Wire (IDs Match)
===SOURCE: utils===
id: "mycompany/utils@v1"
uses: {}
---
def clean(s):
return s.strip()
===SOURCE: processor===
id: "mycompany/processor@v1"
uses: {utils: "mycompany/utils@v1"} # Wants this ID
---
def process(item):
return utils.clean(item.name)
===USE: mycompany/processor===
===USE: mycompany/utils===
# Auto-wired: IDs match exactly
===MAIN===
processor.process(data) # Just works
4.2 Explicit Remap
===SOURCE: my_utils===
id: "my/utils@v2" # Different ID
uses: {}
---
def clean(s):
return s.strip().lower() # Different implementation
===SOURCE: processor===
id: "mycompany/processor@v1"
uses: {utils: "mycompany/utils@v1"} # Wants original
---
def process(item):
return utils.clean(item.name)
===USE: mycompany/processor with {utils: my/utils}=== # Remap here
===USE: my/utils===
===MAIN===
processor.process(data) # Uses my_utils.clean
4.3 Missing Dependency (Error)
===SOURCE: processor===
id: "mycompany/processor@v1"
uses: {utils: "mycompany/utils@v1"}
---
def process(item):
return utils.clean(item.name)
===USE: mycompany/processor===
# No utils provided!
===MAIN===
processor.process(data)
# ERROR: processor requires 'utils' ("mycompany/utils@v1")
# No source with matching ID found
# No explicit binding provided
#
# Add one of:
# ===USE: mycompany/utils===
# ===USE: mycompany/processor with {utils: some/other}===
5. Namespacing
5.1 Qualified Access
All cross-module access is qualified:
===USE: lib/strings===
===USE: lib/numbers===
===MAIN===
# Always prefixed - no ambiguity
clean_name = strings.clean(name)
rounded = numbers.round(value, 2)
5.2 Same-Name Functions
===SOURCE: clearbit===
id: "enrichment/clearbit@v1"
uses: {}
---
def enrich(record):
record.company = api.lookup(record.email)
return record
===SOURCE: linkedin===
id: "enrichment/linkedin@v1"
uses: {}
---
def enrich(record): # Same name - no collision!
record.linkedin = api.find(record.name)
return record
===USE: enrichment/clearbit===
===USE: enrichment/linkedin===
===MAIN===
record = clearbit.enrich(record) # Clear which one
record = linkedin.enrich(record) # Clear which one
5.3 No Global Pollution
# FORBIDDEN in SLOP:
from lib import * # No wildcard imports
import lib # No unqualified imports
# REQUIRED:
===USE: lib/module===
===MAIN===
lib.function() # Always qualified
6. File Organization
6.1 Single File
Everything in one file:
===SOURCE: utils===
id: "myapp/utils@v1"
uses: {}
---
def helper():
pass
===SOURCE: processor===
id: "myapp/processor@v1"
uses: {utils: "myapp/utils@v1"}
---
def process():
return utils.helper()
===MAIN===
processor.process()
6.2 Multi-File Library
mylib/
├── sources/