or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdbinding-dsl.mdcontainer-configuration.mddirect-access.mdindex.mdlazy-property-delegation.mdscoping-and-context.md

lazy-property-delegation.mddocs/

0

# Lazy Property Delegation

1

2

Property delegation pattern for lazy dependency retrieval using the DIAware interface with automatic initialization, caching, and optional triggers for controlling when dependencies are resolved.

3

4

## Capabilities

5

6

### DIAware Interface

7

8

Core interface that enables lazy property delegation for dependency injection with context and trigger support.

9

10

```kotlin { .api }

11

/**

12

* Base interface for classes that use lazy dependency injection

13

*/

14

interface DIAware {

15

/** Reference to the DI container */

16

val di: DI

17

18

/** Context for scoped dependency resolution */

19

val diContext: DIContext<*>

20

21

/** Optional trigger controlling when dependencies are resolved */

22

val diTrigger: DITrigger?

23

}

24

25

/**

26

* Property delegate interface for lazy dependency resolution

27

* @param V Type of the value provided by the delegate

28

*/

29

interface LazyDelegate<V> {

30

/** Retrieve the value (may trigger initialization) */

31

operator fun getValue(thisRef: Any?, property: KProperty<*>): V

32

33

/** Create the property delegate */

34

operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): LazyDelegate<V>

35

36

/** Check if the value has been initialized */

37

fun isInitialized(): Boolean

38

}

39

40

/**

41

* Trigger interface for controlling dependency resolution timing

42

*/

43

interface DITrigger {

44

/** Trigger the resolution process */

45

fun trigger()

46

}

47

```

48

49

### Instance Property Delegates

50

51

Lazy property delegates for retrieving single instances of dependencies with automatic caching and initialization.

52

53

```kotlin { .api }

54

/**

55

* Get a lazy delegate for an instance of type T

56

* @param tag Optional tag for disambiguation

57

* @return LazyDelegate that resolves to an instance of T

58

*/

59

fun <T : Any> DIAware.instance(tag: Any? = null): LazyDelegate<T>

60

61

/**

62

* Get a lazy delegate for an instance with explicit type token

63

* @param type TypeToken representing the type to retrieve

64

* @param tag Optional tag for disambiguation

65

* @return LazyDelegate that resolves to an instance of T

66

*/

67

fun <T : Any> DIAware.Instance(type: TypeToken<out T>, tag: Any? = null): LazyDelegate<T>

68

69

/**

70

* Get a lazy delegate for an instance with factory argument

71

* @param argType TypeToken for the argument type

72

* @param type TypeToken for the return type

73

* @param tag Optional tag for disambiguation

74

* @param arg Function providing the argument for the factory

75

* @return LazyDelegate that resolves to an instance of T

76

*/

77

fun <A, T : Any> DIAware.Instance(

78

argType: TypeToken<in A>,

79

type: TypeToken<T>,

80

tag: Any? = null,

81

arg: () -> A

82

): LazyDelegate<T>

83

84

/**

85

* Get a lazy delegate for a nullable instance

86

* @param tag Optional tag for disambiguation

87

* @return LazyDelegate that resolves to an instance of T or null if not found

88

*/

89

fun <T : Any> DIAware.instanceOrNull(tag: Any? = null): LazyDelegate<T?>

90

91

/**

92

* Get a lazy delegate for a nullable instance with explicit type token

93

* @param type TypeToken representing the type to retrieve

94

* @param tag Optional tag for disambiguation

95

* @return LazyDelegate that resolves to an instance of T or null if not found

96

*/

97

fun <T : Any> DIAware.InstanceOrNull(type: TypeToken<out T>, tag: Any? = null): LazyDelegate<T?>

98

```

99

100

### Provider Property Delegates

101

102

Lazy property delegates for retrieving provider functions that create new instances each time they are called.

103

104

```kotlin { .api }

105

/**

106

* Get a lazy delegate for a provider function

107

* @param tag Optional tag for disambiguation

108

* @return LazyDelegate that resolves to a function () -> T

109

*/

110

fun <T : Any> DIAware.provider(tag: Any? = null): LazyDelegate<() -> T>

111

112

/**

113

* Get a lazy delegate for a provider with explicit type token

114

* @param type TypeToken representing the type to provide

115

* @param tag Optional tag for disambiguation

116

* @return LazyDelegate that resolves to a function () -> T

117

*/

118

fun <T : Any> DIAware.Provider(type: TypeToken<out T>, tag: Any? = null): LazyDelegate<() -> T>

119

120

/**

121

* Get a lazy delegate for a provider curried from a factory

122

* @param argType TypeToken for the argument type

123

* @param type TypeToken for the return type

124

* @param tag Optional tag for disambiguation

125

* @param arg Function providing the argument for currying

126

* @return LazyDelegate that resolves to a provider function

127

*/

128

fun <A, T : Any> DIAware.Provider(

129

argType: TypeToken<in A>,

130

type: TypeToken<out T>,

131

tag: Any? = null,

132

arg: () -> A

133

): DIProperty<() -> T>

134

135

/**

136

* Get a lazy delegate for a nullable provider function

137

* @param tag Optional tag for disambiguation

138

* @return LazyDelegate that resolves to a function (() -> T)? or null if not found

139

*/

140

fun <T : Any> DIAware.providerOrNull(tag: Any? = null): LazyDelegate<(() -> T)?>

141

142

/**

143

* Get a lazy delegate for a nullable provider with explicit type token

144

* @param type TypeToken representing the type to provide

145

* @param tag Optional tag for disambiguation

146

* @return LazyDelegate that resolves to a function (() -> T)? or null if not found

147

*/

148

fun <T : Any> DIAware.ProviderOrNull(type: TypeToken<out T>, tag: Any? = null): LazyDelegate<(() -> T)?>

149

```

150

151

### Factory Property Delegates

152

153

Lazy property delegates for retrieving factory functions that take arguments and create instances.

154

155

```kotlin { .api }

156

/**

157

* Get a lazy delegate for a factory function

158

* @param tag Optional tag for disambiguation

159

* @return LazyDelegate that resolves to a function (A) -> T

160

*/

161

fun <A, T : Any> DIAware.factory(tag: Any? = null): LazyDelegate<(A) -> T>

162

163

/**

164

* Get a lazy delegate for a factory with explicit type tokens

165

* @param argType TypeToken for the argument type

166

* @param type TypeToken for the return type

167

* @param tag Optional tag for disambiguation

168

* @return LazyDelegate that resolves to a function (A) -> T

169

*/

170

fun <A, T : Any> DIAware.Factory(

171

argType: TypeToken<in A>,

172

type: TypeToken<out T>,

173

tag: Any? = null

174

): LazyDelegate<(A) -> T>

175

176

/**

177

* Get a lazy delegate for a nullable factory function

178

* @param tag Optional tag for disambiguation

179

* @return LazyDelegate that resolves to a function ((A) -> T)? or null if not found

180

*/

181

fun <A, T : Any> DIAware.factoryOrNull(tag: Any? = null): LazyDelegate<((A) -> T)?>

182

183

/**

184

* Get a lazy delegate for a nullable factory with explicit type tokens

185

* @param argType TypeToken for the argument type

186

* @param type TypeToken for the return type

187

* @param tag Optional tag for disambiguation

188

* @return LazyDelegate that resolves to a function ((A) -> T)? or null if not found

189

*/

190

fun <A, T : Any> DIAware.FactoryOrNull(

191

argType: TypeToken<in A>,

192

type: TypeToken<out T>,

193

tag: Any? = null

194

): LazyDelegate<((A) -> T)?>

195

```

196

197

### Constant Property Delegates

198

199

Lazy property delegates for retrieving constant values using property names as tags automatically.

200

201

```kotlin { .api }

202

/**

203

* Get a lazy delegate for a constant using the property name as tag

204

* @return LazyDelegate that resolves to the constant value

205

*/

206

fun <T : Any> DIAware.constant(): LazyDelegate<T>

207

208

/**

209

* Get a lazy delegate for a nullable constant using the property name as tag

210

* @return LazyDelegate that resolves to the constant value or null if not found

211

*/

212

fun <T : Any> DIAware.constantOrNull(): LazyDelegate<T?>

213

```

214

215

### Named Property Access

216

217

Wrapper class that automatically uses property names as tags for all dependency lookups.

218

219

```kotlin { .api }

220

/**

221

* Wrapper class that uses property names as tags automatically

222

*/

223

@JvmInline

224

value class Named(private val base: DIAware) : DIAware by base

225

226

/**

227

* Get a named wrapper that uses property names as tags

228

*/

229

val DIAware.named: Named

230

231

// Named delegates (same signatures as DIAware but use property name as tag)

232

fun <T : Any> Named.instance(): LazyDelegate<T>

233

fun <T : Any> Named.provider(): LazyDelegate<() -> T>

234

fun <A, T : Any> Named.factory(): LazyDelegate<(A) -> T>

235

// ... all other delegate methods without tag parameters

236

```

237

238

### Context and Trigger Management

239

240

Advanced property delegation with custom contexts and triggers for fine-grained control over dependency resolution.

241

242

```kotlin { .api }

243

/**

244

* Create a new DIAware instance with different context and/or trigger

245

* @param context New context for scoped dependencies

246

* @param trigger New trigger for controlling resolution timing

247

* @return New DIAware instance with updated context/trigger

248

*/

249

fun DIAware.On(context: DIContext<*> = this.diContext, trigger: DITrigger? = this.diTrigger): DI

250

251

/**

252

* Create a new DIAware instance with typed context

253

* @param context Context value

254

* @param trigger Optional trigger for controlling resolution timing

255

* @return New DIAware instance with typed context

256

*/

257

fun <C : Any> DIAware.on(context: C, trigger: DITrigger? = this.diTrigger): DI

258

259

/**

260

* Create a new instance using dependency injection within a closure

261

* @param creator Function that creates the instance using DirectDI

262

* @return LazyDelegate that creates the instance when accessed

263

*/

264

fun <T> DIAware.newInstance(creator: DirectDI.() -> T): LazyDelegate<T>

265

```

266

267

### Property Delegate Implementation

268

269

Internal implementation classes for property delegation (typically not used directly).

270

271

```kotlin { .api }

272

/**

273

* Property delegate implementation (internal)

274

* @param V Type of the delegated value

275

*/

276

class DIProperty<V>(

277

trigger: DITrigger?,

278

context: DIContext<*>,

279

originalContext: DIContext<*>,

280

f: (DIContext<*>, KProperty<*>) -> V

281

) : LazyDelegate<V>

282

283

/**

284

* Mapped property delegate that transforms values

285

* @param I Input type from the original delegate

286

* @param O Output type after transformation

287

*/

288

class DIPropertyMap<I, O>(

289

private val original: LazyDelegate<I>,

290

private val map: (I) -> O

291

) : LazyDelegate<O>

292

```

293

294

**Usage Examples:**

295

296

```kotlin

297

// Basic DIAware implementation

298

class UserController : DIAware {

299

override val di: DI = appDI

300

301

// Instance delegates - singleton access

302

private val userService: UserService by instance()

303

private val database: Database by instance()

304

305

// Provider delegates - new instance each access

306

private val emailService: () -> EmailService by provider()

307

private val loggerProvider: () -> Logger by provider("fileLogger")

308

309

// Factory delegates - parameterized creation

310

private val userRepositoryFactory: (String) -> UserRepository by factory()

311

private val cacheFactory: (String) -> Cache by factory()

312

313

// Constant delegates using property name as tag

314

private val apiUrl: String by constant()

315

private val timeout: Long by constant()

316

317

// Nullable delegates for optional dependencies

318

private val analyticsService: AnalyticsService? by instanceOrNull()

319

private val debugLogger: (() -> Logger)? by providerOrNull("debug")

320

321

fun handleUser(userId: String) {

322

val service = userService // Singleton instance

323

val email = emailService() // New instance each call

324

val repo = userRepositoryFactory(userId) // Factory with argument

325

val cache = cacheFactory("user:$userId") // Another factory call

326

327

// Use constant values

328

val url = apiUrl // Resolves to constant tagged "apiUrl"

329

val timeoutMs = timeout // Resolves to constant tagged "timeout"

330

331

// Optional services

332

analyticsService?.track("user_accessed", userId)

333

debugLogger?.invoke()?.debug("Processing user $userId")

334

}

335

}

336

337

// Named property access (uses property names as tags)

338

class ConfigService : DIAware {

339

override val di: DI = configDI

340

341

// These automatically use property names as tags

342

private val databaseUrl: String by named.instance()

343

private val apiKey: String by named.instance()

344

private val retryCount: Int by named.instance()

345

private val enableLogging: Boolean by named.instance()

346

}

347

348

// Custom context and triggers

349

class ScopedController : DIAware {

350

override val di: DI = appDI

351

override val diContext: DIContext<*> = diContext<UserSession> { getCurrentSession() }

352

override val diTrigger: DITrigger = object : DITrigger {

353

override fun trigger() {

354

// Custom trigger logic - maybe validate session

355

validateCurrentSession()

356

}

357

}

358

359

// These will use the custom context and trigger

360

private val sessionData: SessionData by instance()

361

private val userPreferences: UserPreferences by instance()

362

363

// Create instances with dependency injection

364

private val processor: RequestProcessor by newInstance {

365

RequestProcessor(instance(), instance(), constant("maxRetries"))

366

}

367

368

// Change context for specific dependencies

369

private val globalCache: Cache by On(AnyDIContext).instance("global")

370

}

371

372

// Late initialization with property delegates

373

class Application : DIAware {

374

private lateinit var _di: DI

375

override val di: DI get() = _di

376

377

// These will be initialized when _di is set

378

private val configService: ConfigService by instance()

379

private val startupTasks: List<StartupTask> by instance()

380

381

fun initialize() {

382

_di = DI {

383

bind<ConfigService>() with singleton { ConfigServiceImpl() }

384

bind<List<StartupTask>>() with provider {

385

listOf(DatabaseMigration(), CacheWarming())

386

}

387

}

388

389

// Now property delegates can resolve

390

configService.load()

391

startupTasks.forEach { it.execute() }

392

}

393

}

394

```