0
# Context-Dependent Wiring
1
2
Context-dependent wiring uses dependencies available in the surrounding context (trait, class, or object scope). This family of functions includes `wire`, `wireRec`, and `wireSet`, each providing different approaches to dependency resolution within the current scope.
3
4
## Core Functions
5
6
### wire
7
8
Creates an instance of type `T` using dependencies from the surrounding context. Dependencies are resolved from values available in the enclosing scope and inherited scopes.
9
10
```scala { .api }
11
def wire[T]: T
12
```
13
14
**Parameters:** None (uses type parameter to determine target type)
15
16
**Returns:** Instance of type `T` with dependencies resolved from context
17
18
**Behavior:**
19
- Looks for values in enclosing trait/class/object
20
- Searches inherited values from parent traits/classes
21
- Uses public primary constructor or companion object `apply` method
22
- Fails at compile time if dependencies cannot be resolved
23
24
### wireRec
25
26
Creates an instance of type `T` with recursive dependency creation. Missing dependencies are automatically created using constructors or `apply` methods.
27
28
```scala { .api }
29
def wireRec[T]: T
30
```
31
32
**Parameters:** None (uses type parameter to determine target type)
33
34
**Returns:** Instance of type `T` with all dependencies created recursively
35
36
**Behavior:**
37
- First attempts to find dependencies in surrounding context like `wire`
38
- Automatically creates missing dependencies using constructors/`apply` methods
39
- Recursively creates nested dependencies as needed
40
- Excludes primitive types and standard library types from automatic creation
41
42
### wireSet
43
44
Collects all instances of the given type available in the surrounding context.
45
46
```scala { .api }
47
def wireSet[T]: Set[T]
48
```
49
50
**Parameters:** None (uses type parameter to determine collection type)
51
52
**Returns:** `Set[T]` containing all available instances of type `T` from context
53
54
**Behavior:**
55
- Searches the enclosing scope for all values of type `T`
56
- Returns a `Set` containing all found instances
57
- The specific `Set` implementation depends on imports in scope (can be mutable or immutable)
58
59
## Usage Examples
60
61
### Basic Context Wiring
62
63
```scala
64
import com.softwaremill.macwire._
65
66
class DatabaseAccess()
67
class SecurityFilter()
68
class UserFinder(databaseAccess: DatabaseAccess, securityFilter: SecurityFilter)
69
70
trait UserModule {
71
// Define dependencies in context
72
lazy val databaseAccess = new DatabaseAccess()
73
lazy val securityFilter = new SecurityFilter()
74
75
// Wire using available context dependencies
76
lazy val userFinder = wire[UserFinder]
77
}
78
```
79
80
### Module Composition with Inheritance
81
82
```scala
83
trait DatabaseModule {
84
lazy val databaseAccess = new DatabaseAccess()
85
}
86
87
trait SecurityModule {
88
lazy val securityFilter = new SecurityFilter()
89
}
90
91
trait UserModule extends DatabaseModule with SecurityModule {
92
// Can wire using dependencies from parent traits
93
lazy val userFinder = wire[UserFinder]
94
lazy val userStatusReader = wire[UserStatusReader]
95
}
96
```
97
98
### Module Composition with Imports
99
100
```scala
101
class DatabaseModule {
102
lazy val databaseAccess = new DatabaseAccess()
103
}
104
105
class UserModule(databaseModule: DatabaseModule) {
106
// Import makes dependencies available to wire
107
import databaseModule._
108
109
lazy val securityFilter = new SecurityFilter()
110
lazy val userFinder = wire[UserFinder]
111
}
112
```
113
114
### Recursive Wiring
115
116
```scala
117
class Service1()
118
class Service2(service1: Service1)
119
class Service3(service1: Service1, service2: Service2)
120
121
trait AppModule {
122
// wireRec creates missing dependencies automatically
123
lazy val service3 = wireRec[Service3]
124
// Equivalent to manually defining:
125
// lazy val service1 = new Service1()
126
// lazy val service2 = new Service2(service1)
127
// lazy val service3 = new Service3(service1, service2)
128
}
129
```
130
131
### Set Collection
132
133
```scala
134
trait Plugin
135
class DatabasePlugin extends Plugin
136
class CachePlugin extends Plugin
137
class MetricsPlugin extends Plugin
138
139
trait PluginModule {
140
lazy val databasePlugin = new DatabasePlugin()
141
lazy val cachePlugin = new CachePlugin()
142
lazy val metricsPlugin = new MetricsPlugin()
143
144
// Collects all Plugin instances
145
lazy val allPlugins: Set[Plugin] = wireSet[Plugin]
146
}
147
```
148
149
### Factory Methods and Apply
150
151
```scala
152
class ConfiguredService private(config: Config)
153
154
object ConfiguredService {
155
def apply(config: Config): ConfiguredService = new ConfiguredService(config)
156
}
157
158
trait ServiceModule {
159
lazy val config = loadConfig()
160
161
// Uses companion object apply method
162
lazy val configuredService = wire[ConfiguredService]
163
}
164
```
165
166
### Scopes and Lifecycle
167
168
```scala
169
trait WebModule {
170
// Singleton scope - same instance reused
171
lazy val singletonService = wire[SingletonService]
172
173
// Prototype scope - new instance each time
174
def prototypeService = wire[PrototypeService]
175
}
176
```
177
178
## Advanced Patterns
179
180
### Conditional Wiring
181
182
```scala
183
trait ConditionalModule {
184
def isDevelopment: Boolean
185
186
lazy val logger = if (isDevelopment) wire[ConsoleLogger] else wire[FileLogger]
187
}
188
```
189
190
### Generic Type Wiring
191
192
```scala
193
class Repository[T](dao: DAO[T])
194
class DAO[T]()
195
196
trait RepositoryModule {
197
lazy val userDao = new DAO[User]()
198
lazy val userRepository = wire[Repository[User]] // Generic types preserved
199
}
200
```
201
202
### Multiple Implementations
203
204
```scala
205
trait EmailService
206
class SmtpEmailService extends EmailService
207
class SendGridEmailService extends EmailService
208
209
trait ServiceModule {
210
// Specify which implementation to wire
211
lazy val emailService: EmailService = wire[SmtpEmailService]
212
}
213
```
214
215
## Scope and Context Rules
216
217
### Dependency Lookup Order
218
219
1. **Current scope values**: `val`, `lazy val`, `def` in current trait/class/object
220
2. **Inherited values**: Values from parent traits and classes
221
3. **Imported values**: Values made available through `import` statements
222
223
### Supported Member Types
224
225
- `val` and `lazy val`: Create singleton dependencies
226
- `def`: Create new instances on each access
227
- Inherited members from parent traits/classes
228
- Imported members from other modules
229
230
### Type Resolution
231
232
- Exact type matching required
233
- Subtype relationships not automatically resolved
234
- Generic type parameters must match exactly
235
- Abstract types resolved using available concrete implementations
236
237
## Error Handling
238
239
### Compile-Time Errors
240
241
- **Missing Dependencies**: "Cannot find a value of type [T]"
242
- **Ambiguous Dependencies**: "Found multiple values of type [T]"
243
- **Unsupported Constructor**: "Cannot find a public constructor"
244
- **Recursive Dependencies**: "Cyclic dependencies detected"
245
246
### Debugging Tips
247
248
- Use explicit type annotations to clarify ambiguous cases
249
- Check that all required dependencies are available in scope
250
- Verify that constructors are public and accessible
251
- Use companion object `apply` methods for complex initialization logic