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

factory-based-wiring.mddocs/

0

# Factory-Based Wiring

1

2

Factory-based wiring uses the `wireWith` family of functions to automatically wire dependencies for factory functions. This approach is ideal when you need custom initialization logic or when working with functions that create instances.

3

4

## Core Functions

5

6

The `wireWith` function family provides overloads for factory functions with 0 to 22 parameters:

7

8

```scala { .api }

9

def wireWith[RES](factory: () => RES): RES

10

def wireWith[A, RES](factory: (A) => RES): RES

11

def wireWith[A, B, RES](factory: (A, B) => RES): RES

12

def wireWith[A, B, C, RES](factory: (A, B, C) => RES): RES

13

def wireWith[A, B, C, D, RES](factory: (A, B, C, D) => RES): RES

14

def wireWith[A, B, C, D, E, RES](factory: (A, B, C, D, E) => RES): RES

15

// ... continues up to 22 parameters

16

def wireWith[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, RES](

17

factory: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V) => RES

18

): RES

19

```

20

21

**Parameters:**

22

- `factory` - Function that creates the desired instance, with parameters that will be wired automatically

23

24

**Returns:** Result of calling the factory function with wired dependencies

25

26

**Behavior:**

27

- Resolves each factory parameter using context-dependent wiring (same as `wire`)

28

- Calls the factory function with all resolved dependencies

29

- Returns the result of the factory function call

30

31

## Usage Examples

32

33

### Basic Factory Wiring

34

35

```scala

36

import com.softwaremill.macwire._

37

38

class DatabaseAccess()

39

class SecurityFilter()

40

class UserFinder(databaseAccess: DatabaseAccess, securityFilter: SecurityFilter)

41

42

trait UserModule {

43

lazy val databaseAccess = new DatabaseAccess()

44

lazy val securityFilter = new SecurityFilter()

45

46

// Factory function for custom initialization

47

def createUserFinder(db: DatabaseAccess, security: SecurityFilter): UserFinder = {

48

println("Creating UserFinder with custom logic")

49

new UserFinder(db, security)

50

}

51

52

// wireWith automatically provides dependencies to the factory

53

lazy val userFinder = wireWith(createUserFinder _)

54

}

55

```

56

57

### Configuration-Based Factories

58

59

```scala

60

case class DatabaseConfig(url: String, maxConnections: Int)

61

class DatabaseAccess(config: DatabaseConfig)

62

63

trait ConfigModule {

64

lazy val dbConfig = DatabaseConfig("jdbc:postgresql://localhost/mydb", 20)

65

66

// Factory with configuration logic

67

def createDatabase(config: DatabaseConfig): DatabaseAccess = {

68

println(s"Connecting to ${config.url}")

69

new DatabaseAccess(config)

70

}

71

72

lazy val databaseAccess = wireWith(createDatabase _)

73

}

74

```

75

76

### Multi-Parameter Factories

77

78

```scala

79

class EmailService(smtpHost: String, port: Int, username: String, password: String)

80

class NotificationService(emailService: EmailService, templateEngine: TemplateEngine)

81

82

trait NotificationModule {

83

lazy val smtpHost = "smtp.example.com"

84

lazy val smtpPort = 587

85

lazy val smtpUsername = "user@example.com"

86

lazy val smtpPassword = "password"

87

lazy val templateEngine = new TemplateEngine()

88

89

// Factory for complex email service setup

90

def createEmailService(host: String, port: Int, user: String, pass: String): EmailService = {

91

val service = new EmailService(host, port, user, pass)

92

service.connect() // Custom initialization

93

service

94

}

95

96

// Factory for notification service with logging

97

def createNotificationService(email: EmailService, template: TemplateEngine): NotificationService = {

98

println("Setting up notification service")

99

new NotificationService(email, template)

100

}

101

102

lazy val emailService = wireWith(createEmailService _)

103

lazy val notificationService = wireWith(createNotificationService _)

104

}

105

```

106

107

### Anonymous Function Factories

108

109

```scala

110

class CacheService(maxSize: Int, ttlSeconds: Int)

111

112

trait CacheModule {

113

lazy val maxCacheSize = 1000

114

lazy val cacheTtl = 3600

115

116

// Inline factory function

117

lazy val cacheService = wireWith { (maxSize: Int, ttl: Int) =>

118

println(s"Creating cache with size $maxSize and TTL $ttl")

119

new CacheService(maxSize, ttl)

120

}

121

}

122

```

123

124

### Higher-Order Factory Functions

125

126

```scala

127

class MetricsCollector()

128

class MonitoredService[T](wrapped: T, metrics: MetricsCollector)

129

130

def wrapWithMonitoring[T](service: T, metrics: MetricsCollector): MonitoredService[T] =

131

new MonitoredService(service, metrics)

132

133

trait MonitoringModule {

134

lazy val metricsCollector = new MetricsCollector()

135

lazy val userService = new UserService()

136

137

// Factory that wraps existing service with monitoring

138

lazy val monitoredUserService = wireWith((service: UserService, metrics: MetricsCollector) =>

139

wrapWithMonitoring(service, metrics))

140

}

141

```

142

143

### Conditional Factory Logic

144

145

```scala

146

trait Logger

147

class ConsoleLogger extends Logger

148

class FileLogger(filename: String) extends Logger

149

150

trait LoggingModule {

151

def isDevelopment: Boolean = sys.props.get("env").contains("dev")

152

lazy val logFilename = "app.log"

153

154

def createLogger(filename: String): Logger = {

155

if (isDevelopment) {

156

new ConsoleLogger()

157

} else {

158

new FileLogger(filename)

159

}

160

}

161

162

lazy val logger = wireWith(createLogger _)

163

}

164

```

165

166

### Factory Composition

167

168

```scala

169

class DatabasePool(url: String, maxConnections: Int)

170

class DatabaseAccess(pool: DatabasePool)

171

class UserRepository(db: DatabaseAccess)

172

173

trait DatabaseModule {

174

lazy val dbUrl = "jdbc:postgresql://localhost/mydb"

175

lazy val maxConnections = 10

176

177

// Nested factory functions

178

def createPool(url: String, max: Int): DatabasePool = {

179

val pool = new DatabasePool(url, max)

180

pool.initialize()

181

pool

182

}

183

184

def createDatabaseAccess(pool: DatabasePool): DatabaseAccess = {

185

new DatabaseAccess(pool)

186

}

187

188

def createUserRepository(db: DatabaseAccess): UserRepository = {

189

new UserRepository(db)

190

}

191

192

lazy val databasePool = wireWith(createPool _)

193

lazy val databaseAccess = wireWith(createDatabaseAccess _)

194

lazy val userRepository = wireWith(createUserRepository _)

195

}

196

```

197

198

## Advanced Patterns

199

200

### Generic Factory Functions

201

202

```scala

203

def createRepository[T](dao: DAO[T]): Repository[T] = new Repository(dao)

204

205

trait RepositoryModule {

206

lazy val userDao = new DAO[User]()

207

208

// Generic factory preserves type parameters

209

lazy val userRepository = wireWith((dao: DAO[User]) => createRepository(dao))

210

}

211

```

212

213

### Implicit Parameter Handling

214

215

```scala

216

class ServiceWithImplicits(config: Config)(implicit ec: ExecutionContext)

217

218

trait ServiceModule {

219

lazy val config = loadConfig()

220

implicit lazy val executionContext: ExecutionContext = ExecutionContext.global

221

222

// Factory handles implicit parameters automatically

223

def createService(conf: Config)(implicit ec: ExecutionContext): ServiceWithImplicits =

224

new ServiceWithImplicits(conf)

225

226

lazy val service = wireWith(createService _)

227

}

228

```

229

230

### Error Handling in Factories

231

232

```scala

233

class DatabaseConnection(url: String) {

234

if (url.isEmpty) throw new IllegalArgumentException("URL cannot be empty")

235

}

236

237

trait DatabaseModule {

238

lazy val dbUrl = "jdbc:postgresql://localhost/mydb"

239

240

def createConnection(url: String): DatabaseConnection = {

241

try {

242

new DatabaseConnection(url)

243

} catch {

244

case e: IllegalArgumentException =>

245

println(s"Invalid database URL: $url")

246

throw e

247

}

248

}

249

250

lazy val connection = wireWith(createConnection _)

251

}

252

```

253

254

## Behavior and Limitations

255

256

### Dependency Resolution

257

258

- Uses the same context-dependent resolution as `wire`

259

- Parameters are resolved from the surrounding scope

260

- Exact type matching required for all parameters

261

262

### Function Types Supported

263

264

- Named functions (`def methodName`)

265

- Function values (`val f = (x: Int) => ...`)

266

- Anonymous functions (`(x: Int) => ...`)

267

- Eta-expanded methods (`methodName _`)

268

269

### Compilation Requirements

270

271

- Available in both Scala 2 and Scala 3

272

- All dependency resolution happens at compile time

273

- Factory functions must have statically analyzable parameter types

274

275

### Error Conditions

276

277

- **Missing Dependencies**: Same as `wire` - compile-time error if factory parameters cannot be resolved

278

- **Unsupported Factory Types**: Complex function types may not be supported

279

- **Parameter Limit**: Maximum 22 parameters due to Scala tuple limitations