🧠 The Search-and-Select Pattern
🧠 The Search-and-Select Pattern
🔍 Multivalued Functions as Possibility
A function that returns iterables instead of a single value can be understood in two powerful ways:
Uncertainty:
The function doesn’t know the answer yet, so it returns several plausible options.Exploration:
When input and output share the same type, the function becomes a map: from one point, it shows all the directions you can go next.
That second interpretation unlocks something fundamental:
A multivalued function is a search space.
🧭 Step by Step Toward a Solution
Think about how you actually solve hard problems.
You don’t jump straight to the solution. You:
- make a rough guess,
- explore variations,
- discard bad paths,
- and gradually converge on something that works.
The Search class models exactly this process.
Each step looks like:
- Pull the most promising candidate from a queue
- Push all neighboring possibilities back into the queue
- Prune the least promising candidates if the queue grows too large
You’re constantly searching and selecting at the same time.
That’s the essence of the Search-and-Select Pattern.
🧰 The Minimal Toolkit
To express this pattern, you only need three concepts:
- A queue — to prioritize candidates
- A space — to describe how candidates expand
- A search — to iterate, filter, and transform results
With just these, you can:
- Define enormous (or infinite) solution spaces without generating them
- Stop as soon as a valid solution appears
- Refine, restrict, or reshape the search declaratively
If this feels familiar, it should.
@fizzwiz/fluentprovides the fluent vocabulary (Each,What)@fizzwiz/sortedsupplies flexible queue strategies@fizzwiz/searchties everything together withSearch
💡 Snippet: Filtering, Mapping, and Resolving a Search
const
search = new Search()
.from(start)
.through(space)
.via(queue),
result = search
.which(predicate)
.sthen(map)
.what();
What’s happening here:
.from(start)— seeds the search.through(space)— defines how candidates expand.via(queue)— controls how candidates are prioritized.which(predicate)— filters unwanted candidates.then(map)— transforms survivors.what()— resolves the search and returns the first valid result
✨ Clean. Composable. Easy to reason about.
🎯 Why This Pattern Matters
When code starts to feel tangled or fragile, it’s often because it’s secretly a search problem — but expressed imperatively.
Once you recognize the pattern, complexity collapses.
What remains is a small set of expressive operations that:
- mirror human reasoning,
- separate intent from mechanics,
- and scale naturally from simple cases to distributed systems.
— @fizzwiz ✨
Comments
Post a Comment