or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

context-dependent-wiring.mdcontext-free-wiring.mddynamic-instance-access.mdfactory-based-wiring.mdindex.md

context-dependent-wiring.mddocs/

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