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

scoping-and-context.mddocs/

0

# Scoping and Context

1

2

Context-aware dependency management with support for hierarchical scopes, context translation, and lifecycle management for fine-grained control over dependency lifecycles and sharing.

3

4

## Capabilities

5

6

### DIContext System

7

8

Core context system for providing scoped dependency resolution with type-safe context management.

9

10

```kotlin { .api }

11

/**

12

* Context definition with type and value for scoped dependency resolution

13

* @param C Type of the context value

14

*/

15

interface DIContext<C : Any> {

16

/** TypeToken representing the context type */

17

val type: TypeToken<in C>

18

19

/** The actual context value */

20

val value: C

21

22

/**

23

* Immediate context implementation with direct value

24

* @param type TypeToken for the context type

25

* @param value The context value

26

*/

27

data class Value<C : Any>(

28

override val type: TypeToken<in C>,

29

override val value: C

30

) : DIContext<C>

31

32

/**

33

* Lazy context implementation with deferred value evaluation

34

* @param type TypeToken for the context type

35

* @param getValue Function that provides the context value when needed

36

*/

37

class Lazy<C : Any>(

38

override val type: TypeToken<in C>,

39

val getValue: () -> C

40

) : DIContext<C> {

41

override val value: C by lazy(getValue)

42

}

43

44

companion object {

45

/**

46

* Create a context with immediate value

47

* @param type TypeToken for the context type

48

* @param value The context value

49

* @return DIContext instance

50

*/

51

operator fun <C : Any> invoke(type: TypeToken<in C>, value: C): DIContext<C>

52

53

/**

54

* Create a context with lazy value evaluation

55

* @param type TypeToken for the context type

56

* @param getValue Function that provides the context value

57

* @return DIContext instance with lazy evaluation

58

*/

59

operator fun <C : Any> invoke(type: TypeToken<in C>, getValue: () -> C): DIContext<C>

60

}

61

}

62

63

/**

64

* Create a DIContext from a typed value

65

* @param context The context value

66

* @return DIContext wrapping the value

67

*/

68

fun <C : Any> diContext(context: C): DIContext<C>

69

70

/**

71

* Create a DIContext with lazy evaluation

72

* @param getContext Function that provides the context value

73

* @return DIContext with lazy evaluation

74

*/

75

fun <C : Any> diContext(getContext: () -> C): DIContext<C>

76

```

77

78

### Scope System

79

80

Comprehensive scoping system for managing instance lifecycles and sharing patterns across different contexts.

81

82

```kotlin { .api }

83

/**

84

* Scope interface for managing instance lifecycles within contexts

85

* @param C Type of the context for this scope

86

*/

87

interface Scope<C> {

88

/**

89

* Get the registry for a specific context

90

* @param context The context value for this scope

91

* @return ScopeRegistry for managing instances in this context

92

*/

93

fun getRegistry(context: C): ScopeRegistry

94

}

95

96

/**

97

* Registry for storing and retrieving scoped instances

98

*/

99

interface ScopeRegistry {

100

/**

101

* Get or create an instance using the provided creator

102

* @param key Unique key for the instance

103

* @param sync Whether to synchronize access

104

* @param creator Function to create the instance if not found

105

* @return The stored or newly created instance

106

*/

107

fun <T> get(key: Any, sync: Boolean, creator: () -> T): T

108

109

/**

110

* Clear all instances from this registry

111

*/

112

fun clear()

113

}

114

115

/**

116

* Multi-item scope registry using concurrent map for thread safety

117

*/

118

class StandardScopeRegistry : ScopeRegistry {

119

// Implementation uses ConcurrentHashMap for thread-safe access

120

}

121

122

/**

123

* Single-item scope registry optimized for performance

124

*/

125

class SingleItemScopeRegistry : ScopeRegistry {

126

// Optimized for scopes that only hold one instance

127

}

128

129

/**

130

* Global scope not bound to any specific context

131

*/

132

class UnboundedScope : Scope<Any> {

133

// Provides global singleton behavior

134

}

135

136

/**

137

* Simple scope implementation without context dependency

138

*/

139

class NoScope : Scope<Any> {

140

// Basic scope without advanced features

141

}

142

143

/**

144

* Hierarchical scope with parent-child relationships

145

* @param C Child context type

146

* @param PC Parent context type

147

*/

148

class SubScope<C : Any, PC : Any>(

149

private val parentScope: Scope<PC>,

150

private val getParentContext: (C) -> PC

151

) : Scope<C>

152

```

153

154

### Scope Builders and Context Binding

155

156

DSL methods for creating scoped binding contexts and managing scope hierarchies.

157

158

```kotlin { .api }

159

/**

160

* Base builder interface for scoped and context bindings

161

* @param C The context type for this builder

162

*/

163

interface DI.BindBuilder<C : Any> {

164

/** The context type for all bindings in this DSL context */

165

val contextType: TypeToken<C>

166

167

/** Whether the context is explicitly set */

168

val explicitContext: Boolean

169

170

/**

171

* Builder with scope support for scoped singletons and multitons

172

* @param C The scope's context type

173

*/

174

interface WithScope<C : Any> : BindBuilder<C> {

175

/** The scope for all bindings in this DSL context */

176

val scope: Scope<C>

177

}

178

}

179

180

/**

181

* Create a scoped binding builder

182

* @param scope The scope to use for bindings

183

* @return BindBuilder with scope support

184

*/

185

fun <C : Any> DI.Builder.scoped(scope: Scope<C>): DI.BindBuilder.WithScope<C>

186

187

/**

188

* Create a context-aware binding builder

189

* @return BindBuilder for the specified context type

190

*/

191

inline fun <reified C : Any> DI.Builder.contexted(): DI.BindBuilder<C>

192

```

193

194

### Context Translation

195

196

Advanced context translation system for converting between different context types and scoped dependency resolution.

197

198

```kotlin { .api }

199

/**

200

* Interface for translating between different context types

201

* @param C Source context type

202

* @param S Target context type

203

*/

204

interface ContextTranslator<C, S> {

205

/**

206

* Translate a context for dependency resolution

207

* @param key The binding key being resolved

208

* @param context The source context

209

* @return Translated context or null if translation not possible

210

*/

211

fun translate(key: DI.Key<*, *, *>, context: C): S?

212

}

213

214

/**

215

* Create a context translator using a transformation function

216

* @param t Function that performs the context translation

217

* @return ContextTranslator instance

218

*/

219

fun <C, S> contextTranslator(t: DirectDI.(C) -> S?): ContextTranslator<C, S>

220

221

/**

222

* Create a context finder that locates context from DirectDI

223

* @param t Function that finds the context

224

* @return ContextTranslator that uses the finder function

225

*/

226

fun <S> contextFinder(t: DirectDI.() -> S): ContextTranslator<Any, S>

227

228

/**

229

* Register a context translator with the DI builder

230

* @param translator The context translator to register

231

*/

232

fun <C, S> DI.Builder.registerContextTranslator(translator: ContextTranslator<C, S>)

233

234

/**

235

* Register a context finder with the DI builder

236

* @param t Function that finds the context

237

*/

238

fun <S> DI.Builder.registerContextFinder(t: DirectDI.() -> S)

239

```

240

241

### JVM-Specific Scopes

242

243

Platform-specific scoping implementations optimized for JVM environments with advanced memory management.

244

245

```kotlin { .api }

246

/**

247

* Scope using weak references for automatic cleanup (JVM only)

248

* @param C Context type for the weak scope

249

*/

250

class WeakContextScope<C : Any> : Scope<C> {

251

// Uses WeakHashMap for automatic context cleanup

252

// when contexts are garbage collected

253

}

254

255

/**

256

* Reference makers for different memory management strategies (JVM only)

257

*/

258

// Thread-local references for thread-scoped singletons

259

val threadLocal: RefMaker

260

261

// Soft references for memory-sensitive caching

262

val softReference: RefMaker

263

264

// Weak references for automatic cleanup

265

val weakReference: RefMaker

266

```

267

268

### Context Operations

269

270

Advanced context manipulation and scoped dependency access patterns for fine-grained dependency control.

271

272

```kotlin { .api }

273

/**

274

* Create a new DIAware with different context

275

* @param context New context for scoped dependencies

276

* @param trigger Optional trigger for dependency resolution

277

* @return New DIAware instance with updated context

278

*/

279

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

280

281

/**

282

* Create a new DIAware with typed context

283

* @param context Context value

284

* @param trigger Optional trigger for dependency resolution

285

* @return New DIAware instance with typed context

286

*/

287

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

288

289

/**

290

* Create a new DirectDI with different context

291

* @param context Context value for scoped dependencies

292

* @return DirectDI instance with the specified context

293

*/

294

fun <C : Any> DirectDIAware.on(context: C): DirectDI

295

```

296

297

**Usage Examples:**

298

299

```kotlin

300

// Define context types

301

data class UserSession(val userId: String, val sessionId: String)

302

data class RequestContext(val requestId: String, val clientId: String)

303

304

// Create scoped DI with multiple contexts

305

val di = DI {

306

// Global singleton (no scope)

307

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

308

309

// Request-scoped dependencies

310

scoped(UnboundedScope).apply {

311

bind<RequestLogger>() with singleton { RequestLoggerImpl() }

312

}

313

314

// User session scoped dependencies

315

contexted<UserSession>().apply {

316

bind<UserPreferences>() with singleton {

317

UserPreferencesImpl(diContext.value.userId)

318

}

319

bind<UserCache>() with singleton {

320

UserCacheImpl(diContext.value.sessionId)

321

}

322

}

323

324

// JVM-specific: Thread-local scope for per-thread singletons

325

bind<Database>() with singleton(ref = threadLocal) {

326

DatabaseConnection()

327

}

328

329

// Context translator for automatic context conversion

330

registerContextTranslator<RequestContext, UserSession> { requestCtx ->

331

// Convert request context to user session context

332

val userId = userService.getUserId(requestCtx.clientId)

333

UserSession(userId, generateSessionId())

334

}

335

}

336

337

// Using scoped dependencies

338

class RequestHandler : DIAware {

339

override val di = di

340

341

fun handleRequest(request: HttpRequest) {

342

val requestContext = RequestContext(request.id, request.clientId)

343

344

// Switch to request context

345

val requestDI = on(requestContext)

346

347

// These use request-scoped context

348

val logger: RequestLogger by requestDI.instance()

349

val userPrefs: UserPreferences by requestDI.instance() // Auto-translated from RequestContext

350

val userCache: UserCache by requestDI.instance()

351

352

logger.info("Processing request ${request.id}")

353

val preferences = userPrefs.load()

354

userCache.store("last_request", request.timestamp)

355

}

356

}

357

358

// Custom scope implementation

359

class TimedScope(private val ttlMs: Long) : Scope<Any> {

360

private val registries = mutableMapOf<Any, TimedScopeRegistry>()

361

362

override fun getRegistry(context: Any): ScopeRegistry {

363

return registries.getOrPut(context) { TimedScopeRegistry(ttlMs) }

364

}

365

366

private class TimedScopeRegistry(private val ttlMs: Long) : ScopeRegistry {

367

private data class TimedEntry<T>(val value: T, val timestamp: Long)

368

private val cache = mutableMapOf<Any, TimedEntry<*>>()

369

370

override fun <T> get(key: Any, sync: Boolean, creator: () -> T): T {

371

val now = System.currentTimeMillis()

372

val entry = cache[key] as? TimedEntry<T>

373

374

return if (entry != null && (now - entry.timestamp) < ttlMs) {

375

entry.value

376

} else {

377

val newValue = creator()

378

cache[key] = TimedEntry(newValue, now)

379

newValue

380

}

381

}

382

383

override fun clear() = cache.clear()

384

}

385

}

386

387

// Using custom scopes

388

val timedDI = DI {

389

val fiveMinuteScope = TimedScope(5 * 60 * 1000) // 5 minutes TTL

390

391

scoped(fiveMinuteScope).apply {

392

bind<ExpensiveService>() with singleton {

393

ExpensiveServiceImpl().also {

394

it.loadData() // Expensive initialization

395

}

396

}

397

}

398

}

399

400

// Hierarchical scoping

401

class ApplicationService : DIAware {

402

override val di = DI {

403

// Application-level scope

404

bind<AppConfig>() with singleton { AppConfigImpl() }

405

406

// Module-level scope (child of application scope)

407

val moduleScope = SubScope(UnboundedScope) { _: ModuleContext -> Unit }

408

scoped(moduleScope).apply {

409

bind<ModuleService>() with singleton { ModuleServiceImpl(instance()) }

410

}

411

412

// Request-level scope (child of module scope)

413

contexted<RequestContext>().apply {

414

bind<RequestProcessor>() with singleton {

415

RequestProcessorImpl(instance(), instance()) // Gets from parent scopes

416

}

417

}

418

}

419

420

private val appConfig: AppConfig by instance() // Application scope

421

422

fun processRequest(requestContext: RequestContext) {

423

val requestDI = on(requestContext)

424

val processor: RequestProcessor by requestDI.instance() // Request scope

425

processor.process()

426

}

427

}

428

429

// JVM-specific weak reference scoping

430

val jvmDI = DI {

431

// Weak context scope - automatically cleans up when contexts are GC'd

432

val weakScope = WeakContextScope<UserSession>()

433

scoped(weakScope).apply {

434

bind<UserData>() with singleton {

435

UserDataImpl(diContext.value.userId)

436

}

437

}

438

439

// Different reference types for memory management

440

bind<ImageCache>() with singleton(ref = softReference) {

441

ImageCacheImpl() // Uses soft references, cleared under memory pressure

442

}

443

444

bind<ThreadContext>() with singleton(ref = threadLocal) {

445

ThreadContextImpl() // One per thread

446

}

447

448

bind<TemporaryData>() with singleton(ref = weakReference) {

449

TemporaryDataImpl() // Automatically cleaned up

450

}

451

}

452

453

// Context translation for complex scenarios

454

val translatingDI = DI {

455

// Register multiple context translators

456

registerContextTranslator<HttpRequest, UserSession> { request ->

457

val session = sessionService.getSession(request.sessionToken)

458

UserSession(session.userId, session.id)

459

}

460

461

registerContextTranslator<UserSession, UserPermissions> { session ->

462

permissionService.getPermissions(session.userId)

463

}

464

465

registerContextFinder<DatabaseConnection> {

466

connectionPool.getConnection()

467

}

468

469

// Bindings that use translated contexts

470

contexted<UserSession>().apply {

471

bind<UserService>() with singleton { UserServiceImpl(diContext.value.userId) }

472

}

473

474

contexted<UserPermissions>().apply {

475

bind<AuthorizationService>() with singleton {

476

AuthorizationServiceImpl(diContext.value)

477

}

478

}

479

}

480

```