# Constructor, explicit, and collection DI **Beginner → Intermediate.** Annotations, explicit targets, collections. Annotate a constructor parameter. Melder builds the graph. What an annotation selects depends on what it is. A **class** selects the spells of that type — a class bound bare, or an existing object of that class — and nothing else: a category that happens to share the class's name does not turn its members into providers. A **Protocol** selects the spells bound under it as their contract. A **`TYPE_CHECKING`-only import**, which leaves a string at runtime, resolves by name to a type or a contract, never to a string category. `list[...]` gathers the group the annotation's kind names: every spell of the class, every implementer of the Protocol, or — for a string — every member of the category or type of that name. ```python import melder as md class Database: """A small stand-in for a database; this example performs no I/O.""" def query(self) -> str: return "42 rows" class ReportService: def __init__(self, database: Database) -> None: # ← injected self.database = database def run(self) -> str: return "report over " + self.database.query() book = md.Spellbook() book.bind(spell=Database, existence="unique") book.bind(spell=ReportService, existence="unique") with book.conjure() as conduit: report: ReportService = conduit.meld("ReportService") assert isinstance(report.database, Database) print(report.run()) ``` This complete example prints `report over 42 rows`: melding the consumer first constructs its registered dependency and injects it into the constructor. When an annotation isn't specific enough — two implementations of one shape, or a named binding — declare the target with `SpellMap` as the parameter default: **Excerpt.** `PrimaryStore` must already be defined and registered in this book. ```python class Consumer: def __init__(self, store=md.SpellMap(PrimaryStore)) -> None: self.store = store ``` `SpellMap` takes the full address: a concrete spell, a `spellframe`, a `binding_name`, or a combination. It obeys an **exactly-one law** — a target that matches zero spells or several fails at `conjure()`, not at runtime. When a single Protocol-annotated parameter has several registered implementations, `conjure()` refuses with an `AMBIGUOUS_PROVIDER` report that lists every candidate's address and the fix — a `SpellMap` default, a meld `override`, or one provider — never a guess. Registering the same class twice is instead refused as a duplicate spell identity at registration; another binding name does not make it a different spell. Ask for a list and get every implementation — the plugin pattern in one annotation: **Excerpt.** Define the `Handler` Protocol and both implementing classes first; bind the dispatcher as well before conjuring. The complete [collection-injection lesson](https://github.com/Synaptic724/melder/blob/prod/UX_and_AIX_experiences/02_intermediate/17_collection_di.py) includes those definitions and prints `['email', 'sms']` from its injected handlers. ```python class Dispatcher: def __init__(self, handlers: list[Handler]) -> None: # ← all of them self.handlers = handlers book.bind(spell=EmailHandler, existence="unique", spellframe=Handler, binding_name="email") book.bind(spell=SmsHandler, existence="unique", spellframe=Handler, binding_name="sms") book.bind(spell=Dispatcher, existence="unique") ``` ## Runnable examples - [Constructor di by annotation](../examples/intermediate/15-constructor-di-by-annotation.md) — Intermediate 15 - [Spellmap defaults](../examples/intermediate/16-spellmap-defaults.md) — Intermediate 16 - [Collection di](../examples/intermediate/17-collection-di.md) — Intermediate 17 [All intermediate examples](../examples/intermediate/index.md) · [Level contents](index.md) · [Full contents](../contents.md) [Canonical page source](https://github.com/Synaptic724/melder/blob/4b364a338debcb1c010d0f6e9dcbbabb62945ba0/README.md)