or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

error-handling.mdindex.mdparallel-processing.mdracing.mdresource-management.mdsynchronization-flow.md

racing.mddocs/

0

# Racing Operations

1

2

Arrow FX Coroutines provides racing operations that allow multiple coroutine blocks to compete, with the first successful completion providing the result while all other blocks are cancelled. This enables powerful patterns for timeouts, fallbacks, and performance optimization.

3

4

## Binary Racing

5

6

### Two-Way Race with Either Result

7

8

```kotlin { .api }

9

suspend fun <A, B> raceN(crossinline fa: suspend CoroutineScope.() -> A, crossinline fb: suspend CoroutineScope.() -> B): Either<A, B>

10

suspend fun <A, B> raceN(ctx: CoroutineContext = EmptyCoroutineContext, crossinline fa: suspend CoroutineScope.() -> A, crossinline fb: suspend CoroutineScope.() -> B): Either<A, B>

11

```

12

13

Race two operations, returning an `Either` indicating which operation won.

14

15

```kotlin

16

val result = raceN(

17

{ slowDatabaseQuery() },

18

{ fastCacheQuery() }

19

)

20

21

when (result) {

22

is Either.Left -> println("Database won: ${result.value}")

23

is Either.Right -> println("Cache won: ${result.value}")

24

}

25

```

26

27

## Ternary Racing

28

29

### Three-Way Race Result Type

30

31

```kotlin { .api }

32

sealed class Race3<out A, out B, out C> {

33

data class First<A>(val winner: A) : Race3<A, Nothing, Nothing>()

34

data class Second<B>(val winner: B) : Race3<Nothing, B, Nothing>()

35

data class Third<C>(val winner: C) : Race3<Nothing, Nothing, C>()

36

37

fun <D> fold(

38

ifFirst: (A) -> D,

39

ifSecond: (B) -> D,

40

ifThird: (C) -> D

41

): D

42

}

43

```

44

45

Result type for three-way races with pattern matching capability.

46

47

### Three-Way Race Operations

48

49

```kotlin { .api }

50

suspend fun <A, B, C> raceN(

51

crossinline fa: suspend CoroutineScope.() -> A,

52

crossinline fb: suspend CoroutineScope.() -> B,

53

crossinline fc: suspend CoroutineScope.() -> C

54

): Race3<A, B, C>

55

56

suspend fun <A, B, C> raceN(

57

ctx: CoroutineContext = EmptyCoroutineContext,

58

crossinline fa: suspend CoroutineScope.() -> A,

59

crossinline fb: suspend CoroutineScope.() -> B,

60

crossinline fc: suspend CoroutineScope.() -> C

61

): Race3<A, B, C>

62

```

63

64

Race three operations with structured result handling.

65

66

```kotlin

67

val result = raceN(

68

{ primaryService.getData() },

69

{ secondaryService.getData() },

70

{ fallbackService.getData() }

71

)

72

73

val data = result.fold(

74

ifFirst = { primary -> "Primary: $primary" },

75

ifSecond = { secondary -> "Secondary: $secondary" },

76

ifThird = { fallback -> "Fallback: $fallback" }

77

)

78

```

79

80

## Advanced Racing with RacingScope

81

82

### RacingScope Interface

83

84

```kotlin { .api }

85

interface RacingScope<in Result> : CoroutineScope {

86

fun raceOrThrow(

87

context: CoroutineContext = EmptyCoroutineContext,

88

block: suspend CoroutineScope.() -> Result

89

)

90

}

91

```

92

93

Advanced racing scope that allows building complex racing scenarios.

94

95

### Racing Scope Execution

96

97

```kotlin { .api }

98

suspend fun <A> racing(block: RacingScope<A>.() -> Unit): A

99

```

100

101

Create a racing scope where multiple operations can compete.

102

103

```kotlin

104

val winner = racing<String> {

105

raceOrThrow { "Fast operation completed" }

106

raceOrThrow {

107

delay(1000)

108

"Slow operation completed"

109

}

110

raceOrThrow {

111

delay(500)

112

"Medium operation completed"

113

}

114

}

115

```

116

117

### Racing with Exception Handling

118

119

```kotlin { .api }

120

fun <Result> RacingScope<Result>.race(

121

context: CoroutineContext = EmptyCoroutineContext,

122

condition: (Result) -> Boolean = { true },

123

block: suspend CoroutineScope.() -> Result

124

)

125

```

126

127

Race with custom exception handling and success conditions.

128

129

```kotlin

130

val result = racing<String?> {

131

race(condition = { it != null }) {

132

unreliableService.getData() // May return null

133

}

134

135

race {

136

delay(5000)

137

"Timeout fallback"

138

}

139

}

140

```

141

142

## Racing with Error Handling

143

144

### RaiseScope Integration

145

146

```kotlin { .api }

147

interface RaiseScope<in Error> : CoroutineScope, Raise<Error>

148

typealias RaiseHandler<Error> = (context: CoroutineContext, error: Error) -> Unit

149

```

150

151

Racing operations that integrate with Arrow's Raise error handling system.

152

153

### Racing with Raise Error Handling

154

155

```kotlin { .api }

156

fun <Result> RacingScope<Result>.raceOrThrow(

157

raise: RaiseHandler<Error>,

158

condition: (Result) -> Boolean = { true },

159

block: suspend RaiseScope<Error>.() -> Result

160

)

161

162

fun <Result> RacingScope<Result>.race(

163

context: CoroutineContext = EmptyCoroutineContext,

164

raise: RaiseHandler<Error>,

165

condition: (Result) -> Boolean = { true },

166

block: suspend RaiseScope<Error>.() -> Result

167

)

168

```

169

170

Race operations with structured error handling.

171

172

```kotlin

173

val result = either<ServiceError, String> {

174

racing<String> {

175

raceOrThrow(raise = { error -> raise(error) }) {

176

serviceA.getData() // Can raise ServiceError

177

}

178

179

raceOrThrow(raise = { error -> raise(error) }) {

180

serviceB.getData() // Can raise ServiceError

181

}

182

}

183

}

184

```

185

186

## Common Racing Patterns

187

188

### Timeout Pattern

189

190

```kotlin

191

suspend fun <T> withTimeout(timeoutMs: Long, operation: suspend () -> T): T? {

192

return raceN(

193

{ operation() },

194

{

195

delay(timeoutMs)

196

null

197

}

198

).fold(

199

{ result -> result },

200

{ null }

201

)

202

}

203

204

val result = withTimeout(5000) {

205

slowNetworkCall()

206

}

207

```

208

209

### Fallback Pattern

210

211

```kotlin

212

suspend fun <T> withFallback(

213

primary: suspend () -> T,

214

fallback: suspend () -> T

215

): T {

216

return raceN(

217

{ primary() },

218

{

219

delay(3000) // Wait before trying fallback

220

fallback()

221

}

222

).fold(

223

{ it },

224

{ it }

225

)

226

}

227

```

228

229

### Circuit Breaker Pattern

230

231

```kotlin

232

class CircuitBreaker<T> {

233

suspend fun executeWithFallback(

234

primary: suspend () -> T,

235

fallback: suspend () -> T

236

): T = racing {

237

raceOrThrow(condition = { isServiceHealthy() }) {

238

primary()

239

}

240

241

race {

242

delay(100) // Small delay before fallback

243

fallback()

244

}

245

}

246

}

247

```

248

249

### Multiple Service Racing

250

251

```kotlin

252

suspend fun <T> raceMultipleServices(

253

services: List<suspend () -> T>

254

): T = racing {

255

services.forEach { service ->

256

raceOrThrow { service() }

257

}

258

}

259

260

val data = raceMultipleServices(

261

listOf(

262

{ serviceA.getData() },

263

{ serviceB.getData() },

264

{ serviceC.getData() }

265

)

266

)

267

```

268

269

## Performance and Cancellation

270

271

### Cancellation Behavior

272

273

All racing operations properly handle cancellation:

274

275

- When one operation completes, all other operations are immediately cancelled

276

- Resources are properly cleaned up in cancelled operations

277

- Parent scope cancellation is propagated to all racing operations

278

279

### Resource Management in Racing

280

281

```kotlin

282

val result = racing<String> {

283

raceOrThrow {

284

resourceScope {

285

val connection = connectionResource.bind()

286

connection.query("SELECT data FROM table1")

287

}

288

}

289

290

raceOrThrow {

291

resourceScope {

292

val cache = cacheResource.bind()

293

cache.get("data_key")

294

}

295

}

296

}

297

// Losing operation's resources are automatically cleaned up

298

```

299

300

### Exception Propagation

301

302

Racing operations handle exceptions according to structured concurrency principles:

303

304

```kotlin

305

val result = try {

306

racing<String> {

307

raceOrThrow {

308

throw IllegalStateException("Service A failed")

309

}

310

311

raceOrThrow {

312

delay(100)

313

"Service B success"

314

}

315

}

316

} catch (e: IllegalStateException) {

317

"Handled failure: ${e.message}"

318

}

319

```

320

321

## Integration with Other Patterns

322

323

### Racing with Parallel Processing

324

325

```kotlin

326

val results = urls.parMap { url ->

327

raceN(

328

{ primaryClient.get(url) },

329

{

330

delay(2000)

331

fallbackClient.get(url)

332

}

333

).fold({ it }, { it })

334

}

335

```

336

337

### Racing with Resource Management

338

339

```kotlin

340

resourceScope {

341

val config = configResource.bind()

342

343

val data = racing<String> {

344

config.endpoints.forEach { endpoint ->

345

raceOrThrow {

346

httpClient.get(endpoint)

347

}

348

}

349

}

350

351

processData(data)

352

}

353

```