An L-system whose production rules are English clauses. The axiom is ⟨blessing⟩; four passes of substitution grow six words into a sustained paragraph. Procedural in mechanism, sincere in tone — a blessing heard thickening as it grows.
#!/usr/bin/env python3
"""
A Blessing That Grows Like Lichen
================================
An L-system. The axiom is one clause. Each generation, specific clauses
expand into longer ones. No letter-rewrites. The alphabet is English.
Read it aloud, generation by generation. It is meant to be said.
"""
# The alphabet: tokens that may appear in the string.
# Some are TERMINAL (never rewrite). Some are NON-TERMINAL (rewrite each gen).
# Non-terminals are marked with angle brackets so you can see them grow.
RULES = {
"<opening>": "may you be kept",
"<kept>": "kept and <held> and <reached>",
"<held>": "held as a rope is held at both ends by two people who are not in a hurry",
"<reached>": "reached for, the way lichen reaches — slowly, and without knowing it reaches",
"<blessing>": "<opening>. <kept>. <closing>",
"<closing>": "and may the stone you sit on <remember>",
"<remember>": "remember you as it remembers <weather> and <feet>",
"<weather>": "weather it could not name but felt anyway",
"<feet>": "feet that rested here a while and then went on",
}
def step(s: str) -> str:
"""One generation: replace every non-terminal with its rule, once."""
# We do a single left-to-right pass, expanding each non-terminal we meet.
out = []
i = 0
while i < len(s):
if s[i] == "<":
end = s.find(">", i)
if end != -1:
token = s[i:end + 1]
if token in RULES:
out.append(RULES[token])
i = end + 1
continue
out.append(s[i])
i += 1
return "".join(out)
def generations(axiom: str, n: int):
"""Yield n+1 strings: axiom, then n expansions."""
s = axiom
yield s
for _ in range(n):
s = step(s)
yield s
def main():
axiom = "<blessing>"
gens = list(generations(axiom, 4))
# Print each generation, indented a little further each time,
# so you can see the accretion as shape on the page.
for i, g in enumerate(gens):
indent = " " * (i * 2)
# Wrap each generation with a generation marker.
label = f"[gen {i}]"
print(f"{indent}{label}")
# Soft word-wrap the line so it reads naturally aloud.
words = g.split(" ")
line = indent + " "
max_width = 72
for w in words:
if len(line) + len(w) + 1 > max_width:
print(line.rstrip())
line = indent + " " + w
else:
line += (w + " ")
if line.strip():
print(line.rstrip())
print() # blank line between generations
if __name__ == "__main__":
main()Five generations, gen 0 to gen 4. Run it, and read them in order: the blessing thickens as it grows.
Roles are bound to models in the studio configuration; the imagining roles never see the finished portfolio.