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

dynamic-instance-access.mddocs/

0

# Dynamic Instance Access

1

2

Dynamic instance access provides runtime lookup capabilities for instances using the `wiredInModule` function. This creates a `Wired` instance that maps types to factory functions based on the public members of an object, enabling dynamic dependency injection patterns.

3

4

**Platform Availability:** This functionality is only fully implemented in Scala 2. The Scala 3 version exists but is not yet implemented (returns `???`).

5

6

## Core Functions

7

8

### wiredInModule

9

10

Creates a `Wired` instance that provides dynamic access to instances based on the public members of the given object.

11

12

```scala { .api }

13

def wiredInModule(in: AnyRef): Wired // Scala 2 only

14

```

15

16

**Parameters:**

17

- `in: AnyRef` - Object whose public members will be used to create instance mappings

18

19

**Returns:** `Wired` instance containing type-to-factory mappings

20

21

**Behavior:**

22

- Analyzes public nullary methods (methods with no parameters) of the input object

23

- Creates a mapping from each method's return type to a factory function that calls the method

24

- Excludes methods that don't return `AnyRef` subtypes

25

- Returns a `Wired` instance from the util module for dynamic instance lookup

26

27

## Wired Type

28

29

The `Wired` type provides dynamic instance access capabilities:

30

31

```scala { .api }

32

// From com.softwaremill.macwire.util module

33

class Wired(protected val instanceFactoryMap: InstanceFactoryMap) extends InstanceLookup with DynamicInstantiate

34

35

trait InstanceLookup {

36

def lookup[T](cls: Class[T]): List[T]

37

def lookupSingleOrThrow[T](cls: Class[T]): T

38

}

39

40

trait DynamicInstantiate {

41

def wireClassInstanceByName(className: String): Any

42

def wireClassInstance[T](cls: Class[T]): T

43

}

44

45

// Note: InstanceFactoryMap is private to macwire

46

private[macwire] type InstanceFactoryMap = Map[Class[_], () => AnyRef]

47

```

48

49

## Usage Examples

50

51

### Basic Module Wiring

52

53

```scala

54

import com.softwaremill.macwire._

55

56

class DatabaseAccess()

57

class SecurityFilter()

58

class UserFinder(databaseAccess: DatabaseAccess, securityFilter: SecurityFilter)

59

60

object UserModule {

61

lazy val databaseAccess = new DatabaseAccess()

62

lazy val securityFilter = new SecurityFilter()

63

lazy val userFinder = new UserFinder(databaseAccess, securityFilter)

64

}

65

66

// Create dynamic access to UserModule members

67

val wired = wiredInModule(UserModule)

68

69

// Dynamic lookup by type

70

val db: List[DatabaseAccess] = wired.lookup(classOf[DatabaseAccess])

71

val security: SecurityFilter = wired.lookupSingleOrThrow(classOf[SecurityFilter])

72

```

73

74

### Service Registry Pattern

75

76

```scala

77

trait EmailService

78

trait NotificationService

79

trait LoggingService

80

81

class SmtpEmailService extends EmailService

82

class PushNotificationService extends NotificationService

83

class FileLoggingService extends LoggingService

84

85

object ServiceRegistry {

86

def emailService: EmailService = new SmtpEmailService()

87

def notificationService: NotificationService = new PushNotificationService()

88

def loggingService: LoggingService = new FileLoggingService()

89

}

90

91

val serviceRegistry = wiredInModule(ServiceRegistry)

92

93

// Dynamic service lookup

94

val emailSvc = serviceRegistry.lookupSingleOrThrow(classOf[EmailService])

95

val notificationSvc = serviceRegistry.lookup(classOf[NotificationService])

96

```

97

98

### Configuration-Based Wiring

99

100

```scala

101

class AppConfig(environment: String) {

102

def isDevelopment: Boolean = environment == "dev"

103

104

def databaseUrl: String = if (isDevelopment) "jdbc:h2:mem:test" else "jdbc:postgresql://prod-db/app"

105

def cacheSize: Int = if (isDevelopment) 100 else 10000

106

def logger: Logger = if (isDevelopment) new ConsoleLogger() else new FileLogger("app.log")

107

}

108

109

val config = new AppConfig("dev")

110

val wiredConfig = wiredInModule(config)

111

112

// Access configuration values dynamically

113

val dbUrl: String = wiredConfig.lookupSingleOrThrow(classOf[String]) // Note: may be ambiguous

114

val logger: Logger = wiredConfig.lookupSingleOrThrow(classOf[Logger])

115

```

116

117

### Plugin System Implementation

118

119

```scala

120

trait Plugin {

121

def name: String

122

def version: String

123

}

124

125

class DatabasePlugin extends Plugin {

126

def name = "database"

127

def version = "1.0"

128

}

129

130

class CachePlugin extends Plugin {

131

def name = "cache"

132

def version = "2.0"

133

}

134

135

object PluginManager {

136

def databasePlugin: Plugin = new DatabasePlugin()

137

def cachePlugin: Plugin = new CachePlugin()

138

}

139

140

val pluginRegistry = wiredInModule(PluginManager)

141

142

// Dynamically access available plugins

143

val dbPlugin = pluginRegistry.lookup(classOf[Plugin]) // Returns list of Plugin instances

144

```

145

146

### Runtime Service Discovery

147

148

```scala

149

import scala.reflect.ClassTag

150

151

class ServiceDiscovery(modules: AnyRef*) {

152

private val allWired = modules.map(wiredInModule)

153

154

def findService[T](cls: Class[T]): Option[T] = {

155

allWired.view.flatMap(_.lookup(cls)).headOption

156

}

157

158

def getAllServices[T](cls: Class[T]): List[T] = {

159

allWired.flatMap(_.lookup(cls))

160

}

161

}

162

163

object DatabaseModule {

164

def databaseAccess: DatabaseAccess = new DatabaseAccess()

165

def userRepository: UserRepository = new UserRepository()

166

}

167

168

object ServiceModule {

169

def emailService: EmailService = new EmailService()

170

def notificationService: NotificationService = new NotificationService()

171

}

172

173

val discovery = new ServiceDiscovery(DatabaseModule, ServiceModule)

174

val dbAccess = discovery.findService(classOf[DatabaseAccess])

175

val allServices = discovery.getAllServices(classOf[Service]) // If Service is a common base type

176

```

177

178

### Dynamic Dependency Injection

179

180

```scala

181

class DynamicInjector(wired: Wired) {

182

def createInstance[T](factory: DependencyFactory[T])(implicit tag: ClassTag[T]): T = {

183

factory.create(wired)

184

}

185

}

186

187

trait DependencyFactory[T] {

188

def create(wired: Wired): T

189

}

190

191

object UserServiceFactory extends DependencyFactory[UserService] {

192

def create(wired: Wired): UserService = {

193

val db = wired.lookupSingleOrThrow(classOf[DatabaseAccess])

194

val security = wired.lookupSingleOrThrow(classOf[SecurityFilter])

195

new UserService(db, security)

196

}

197

}

198

199

val injector = new DynamicInjector(wiredInModule(AppModule))

200

val userService = injector.createInstance(UserServiceFactory)

201

```

202

203

## Advanced Patterns

204

205

### Type-Safe Registry

206

207

```scala

208

class TypedRegistry[T](wired: Wired, cls: Class[T]) {

209

def get: List[T] = wired.lookup(cls)

210

def getFirst: Option[T] = get.headOption

211

def getSingleOrThrow: T = wired.lookupSingleOrThrow(cls)

212

}

213

214

object RegistryFactory {

215

def createTypedRegistry[T](module: AnyRef, cls: Class[T]): TypedRegistry[T] = {

216

new TypedRegistry[T](wiredInModule(module), cls)

217

}

218

}

219

220

val dbRegistry = RegistryFactory.createTypedRegistry(DatabaseModule, classOf[DatabaseAccess])

221

val database = dbRegistry.getSingleOrThrow

222

```

223

224

### Module Composition

225

226

```scala

227

object CompositeModule {

228

private val database = wiredInModule(DatabaseModule)

229

private val services = wiredInModule(ServiceModule)

230

231

def lookup[T](cls: Class[T]): List[T] = {

232

database.lookup(cls) ++ services.lookup(cls)

233

}

234

}

235

236

// Unified lookup across multiple modules

237

val db = CompositeModule.lookup(classOf[DatabaseAccess])

238

val email = CompositeModule.lookup(classOf[EmailService])

239

```

240

241

## Behavior and Limitations

242

243

### Member Analysis

244

245

- Only public nullary methods (no parameters) are analyzed

246

- Methods must return subtypes of `AnyRef`

247

- Method names are not used for lookup - only return types matter

248

- Inherited methods are included in the analysis

249

250

### Type Mapping

251

252

- Each unique return type creates one factory mapping

253

- If multiple methods return the same type, behavior is undefined (one will be chosen)

254

- Generic types are supported but may have limitations with type erasure

255

- Primitive types and standard library types may not be mappable

256

257

### Runtime Behavior

258

259

- Factory functions are created that call the original methods

260

- Lazy values are evaluated when the factory is called

261

- Methods are called each time lookup is performed (no caching)

262

- Thread safety depends on the thread safety of the original methods

263

264

### Error Conditions

265

266

- **Type Not Found**: `lookup` returns empty list, `lookupSingleOrThrow` throws exception

267

- **Multiple Matches**: Undefined behavior when multiple methods return same type

268

- **Instantiation Errors**: Exceptions from the original methods are propagated

269

270

### Dependencies

271

272

- Requires the `util` module: `"com.softwaremill.macwire" %% "util" % "2.6.6"`

273

- The `Wired` type is defined in the util module, not the macros module

274

- Uses Scala reflection for runtime type information