or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

collection-extensions.mdcore-data-types.mdeffect-system.mdindex.mdraise-dsl.mdtuple-types.md

raise-dsl.mddocs/

0

# Raise DSL

1

2

Composable typed error handling system with short-circuiting and error accumulation capabilities. The Raise DSL provides a unified approach to error handling that integrates seamlessly with Arrow's data types.

3

4

## Capabilities

5

6

### Raise<Error>

7

8

Core interface for typed error handling that allows raising errors and binding over various error-containing types.

9

10

```kotlin { .api }

11

/**

12

* Core interface for typed error handling

13

* @param Error The type of errors that can be raised

14

*/

15

@RaiseDSL

16

interface Raise<in Error> {

17

/**

18

* Raise an error, short-circuiting the computation

19

* @param error The error to raise

20

* @return Nothing (never returns)

21

*/

22

fun raise(error: Error): Nothing

23

24

/**

25

* Extract the success value from Either, or raise the error

26

* @param receiver Either to bind over

27

* @return Success value if Right, raises error if Left

28

*/

29

fun <A> Either<Error, A>.bind(): A

30

31

/**

32

* Extract value from Option, raising Unit if None (requires SingletonRaise)

33

* @param receiver Option to bind over

34

* @return Value if Some, raises if None

35

*/

36

fun <A> Option<A>.bind(): A // Available in SingletonRaise context

37

38

/**

39

* Extract success value from Ior, accumulating any left values

40

* @param receiver Ior to bind over

41

* @return Right or Both right value, accumulates left values

42

*/

43

fun <A> Ior<Error, A>.bind(): A // Available in IorRaise context

44

45

/**

46

* Bind over all Either values in a collection

47

* @param receiver Collection of Either values

48

* @return Collection of success values, raises first error encountered

49

*/

50

fun <A> Iterable<Either<Error, A>>.bindAll(): List<A>

51

fun <A> NonEmptyList<Either<Error, A>>.bindAll(): NonEmptyList<A>

52

fun <A> NonEmptySet<Either<Error, A>>.bindAll(): NonEmptySet<A>

53

fun <K, A> Map<K, Either<Error, A>>.bindAll(): Map<K, A>

54

}

55

```

56

57

**Usage Examples:**

58

59

```kotlin

60

import arrow.core.raise.*

61

import arrow.core.*

62

63

// Basic error raising

64

fun Raise<String>.parsePositiveInt(input: String): Int {

65

val number = input.toIntOrNull() ?: raise("Not a valid integer: $input")

66

if (number <= 0) raise("Number must be positive: $number")

67

return number

68

}

69

70

// Using Either DSL

71

val result = either {

72

val x = parsePositiveInt("42") // 42

73

val y = parsePositiveInt("0") // Raises "Number must be positive: 0"

74

x + y // This line never executes

75

} // Left("Number must be positive: 0")

76

77

// Binding over Either values

78

val computation = either {

79

val a = Either.right(10).bind() // 10

80

val b = Either.right(20).bind() // 20

81

val c = Either.left("error").bind() // Raises "error"

82

a + b + c // Never executes

83

} // Left("error")

84

```

85

86

### SingletonRaise<Error>

87

88

Specialized Raise implementation for handling nullable values and Option types.

89

90

```kotlin { .api }

91

/**

92

* Specialized Raise for unit-based errors, typically for null handling

93

* @param Error Error type (often Unit for simple null checks)

94

*/

95

class SingletonRaise<in Error>(private val raise: Raise<Unit>) : Raise<Error> {

96

/**

97

* Ensure a condition is true, raising unit error if false

98

* @param condition Boolean condition to check

99

*/

100

fun ensure(condition: Boolean): Unit

101

102

/**

103

* Ensure a value is not null, raising unit error if null

104

* @param value Nullable value to check

105

* @return Non-null value

106

*/

107

fun <A> ensureNotNull(value: A?): A

108

109

/**

110

* Bind over nullable values

111

* @param receiver Nullable value

112

* @return Non-null value or raises

113

*/

114

fun <A> A?.bind(): A

115

116

/**

117

* Bind over Option values

118

* @param receiver Option to bind over

119

* @return Value if Some, raises if None

120

*/

121

fun <A> Option<A>.bind(): A

122

123

/**

124

* Bind over collections of nullable values

125

*/

126

fun <A> Iterable<A?>.bindAll(): List<A>

127

fun <A> NonEmptyList<A?>.bindAll(): NonEmptyList<A>

128

fun <K, A> Map<K, A?>.bindAll(): Map<K, A>

129

130

/**

131

* Recover from errors with fallback computation

132

*/

133

inline fun <A> recover(

134

block: SingletonRaise<Error>.() -> A,

135

fallback: () -> A

136

): A

137

}

138

```

139

140

**Usage Examples:**

141

142

```kotlin

143

import arrow.core.raise.*

144

import arrow.core.*

145

146

// Working with nullable values

147

val result = option {

148

val name = "Alice".takeIf { it.isNotEmpty() }.bind() // "Alice"

149

val age = "25".toIntOrNull().bind() // 25

150

val email = null.bind() // Raises, short-circuits

151

"$name ($age) - $email" // Never executes

152

} // None

153

154

// Validation with ensure

155

val validation = option {

156

val input = "42"

157

ensure(input.isNotBlank()) { } // Passes

158

val number = input.toIntOrNull().bind()

159

ensureNotNull(number.takeIf { it > 0 }) // 42

160

} // Some(42)

161

162

// Binding collections

163

val numbers = option {

164

listOf("1", "2", null, "4").mapNotNull { it?.toIntOrNull() }.bind() // Fails on null

165

} // None

166

```

167

168

### IorRaise<Error>

169

170

Raise implementation that accumulates errors while preserving successful computations.

171

172

```kotlin { .api }

173

/**

174

* Raise implementation for error accumulation with Ior

175

* @param Error Error type that can be combined

176

*/

177

class IorRaise<Error>(

178

val combineError: (Error, Error) -> Error,

179

private val combineRef: Atomic<Any?>,

180

private val raise: Raise<Error>

181

) : Raise<Error> {

182

/**

183

* Accumulate an error without short-circuiting

184

* @param error Error to accumulate

185

*/

186

fun accumulate(error: Error): Unit

187

188

/**

189

* Combine an error with any previously accumulated errors

190

* @param error Error to combine

191

* @return Combined error

192

*/

193

fun combine(error: Error): Error

194

195

/**

196

* Bind over Ior values, accumulating left values

197

* @param receiver Ior to bind over

198

* @return Right value, accumulates any left values

199

*/

200

fun <A> Ior<Error, A>.bind(): A

201

202

/**

203

* Get value from Either or accumulate the error and provide fallback

204

* @param receiver Either to process

205

* @param fallback Function to provide fallback value

206

* @return Right value or result of fallback after accumulating error

207

*/

208

fun <A> Either<Error, A>.getOrAccumulate(fallback: (Error) -> A): A

209

210

/**

211

* Recover from accumulated errors

212

*/

213

inline fun <A> recover(

214

block: IorRaise<Error>.() -> A,

215

fallback: (Error) -> A

216

): A

217

}

218

```

219

220

**Usage Examples:**

221

222

```kotlin

223

import arrow.core.raise.*

224

import arrow.core.*

225

226

// Error accumulation with IorRaise

227

data class ValidationError(val message: String)

228

229

fun combineErrors(e1: ValidationError, e2: ValidationError): ValidationError =

230

ValidationError("${e1.message}; ${e2.message}")

231

232

val result = ior(::combineErrors) {

233

val name = validateName("").getOrAccumulate { ValidationError("Name is required") }

234

val email = validateEmail("invalid").getOrAccumulate { ValidationError("Invalid email") }

235

val age = validateAge(-1).getOrAccumulate { ValidationError("Age must be positive") }

236

237

User(name, email, age) // Creates user even with accumulated errors

238

} // Ior.Both(ValidationError("Name is required; Invalid email; Age must be positive"), User(...))

239

```

240

241

### DSL Builder Functions

242

243

Convenient functions for creating computations in the Raise context.

244

245

```kotlin { .api }

246

/**

247

* Create an Either computation with error handling

248

* @param block Computation that can raise errors of type Error

249

* @return Either.Left with error or Either.Right with result

250

*/

251

inline fun <Error, A> either(block: Raise<Error>.() -> A): Either<Error, A>

252

253

/**

254

* Create an Option computation with null handling

255

* @param block Computation that can fail (raise Unit)

256

* @return Some with result or None if computation failed

257

*/

258

inline fun <A> option(block: SingletonRaise<Unit>.() -> A): Option<A>

259

260

/**

261

* Create an Ior computation with error accumulation

262

* @param combineError Function to combine multiple errors

263

* @param block Computation that can raise and accumulate errors

264

* @return Ior with accumulated errors and/or result

265

*/

266

inline fun <Error, A> ior(

267

combineError: (Error, Error) -> Error,

268

block: IorRaise<Error>.() -> A

269

): Ior<Error, A>

270

271

/**

272

* Create a nullable computation

273

* @param block Computation that can fail

274

* @return Result value or null if computation failed

275

*/

276

inline fun <A> nullable(block: SingletonRaise<Unit>.() -> A): A?

277

278

/**

279

* Recover from errors in Either computation

280

* @param block Computation that might fail

281

* @param fallback Function to handle errors

282

* @return Result of computation or fallback

283

*/

284

inline fun <Error, A> recover(

285

block: Raise<Error>.() -> A,

286

fallback: (Error) -> A

287

): A

288

289

/**

290

* Fold over Either result with error and success handlers

291

* @param block Computation that might fail

292

* @param onError Handler for error case

293

* @param onSuccess Handler for success case

294

* @return Result of appropriate handler

295

*/

296

inline fun <Error, A, B> fold(

297

block: Raise<Error>.() -> A,

298

onError: (Error) -> B,

299

onSuccess: (A) -> B

300

): B

301

```

302

303

**Usage Examples:**

304

305

```kotlin

306

import arrow.core.raise.*

307

import arrow.core.*

308

309

// Either computation

310

val parseResult = either {

311

val input = "not-a-number"

312

input.toIntOrNull() ?: raise("Invalid number: $input")

313

} // Left("Invalid number: not-a-number")

314

315

// Option computation with nullable handling

316

val safeComputation = option {

317

val user = findUser("123").bind() // Returns null if not found

318

val profile = user.profile.bind() // Returns null if no profile

319

profile.displayName.bind() // Returns null if no display name

320

} // None if any step returns null

321

322

// Recovery with fallback

323

val withFallback = recover(

324

{ parsePositiveInt("-5") } // Raises error

325

) { error -> 0 } // 0

326

327

// Folding over computation

328

val message = fold(

329

{ parsePositiveInt("42") },

330

onError = { "Error: $it" },

331

onSuccess = { "Success: $it" }

332

) // "Success: 42"

333

```

334

335

### Effect System

336

337

Suspended computations that can raise typed errors, providing a foundation for asynchronous and concurrent programming with proper error handling.

338

339

```kotlin { .api }

340

/**

341

* Suspended computation that can raise typed errors

342

* @param Error The type of errors that can be raised

343

* @param A The type of successful result

344

*/

345

typealias Effect<Error, A> = suspend Raise<Error>.() -> A

346

347

/**

348

* Non-suspended computation that can raise typed errors

349

* @param Error The type of errors that can be raised

350

* @param A The type of successful result

351

*/

352

typealias EagerEffect<Error, A> = Raise<Error>.() -> A

353

354

/**

355

* Execute a suspended Effect computation

356

* @param block The Effect computation to execute

357

* @return Either containing the error or successful result

358

*/

359

suspend fun <Error, A> effect(block: suspend Raise<Error>.() -> A): Either<Error, A>

360

361

/**

362

* Execute an eager Effect computation

363

* @param block The EagerEffect computation to execute

364

* @return Either containing the error or successful result

365

*/

366

fun <Error, A> eagerEffect(block: Raise<Error>.() -> A): Either<Error, A>

367

368

/**

369

* Execute an Effect and fold over the result

370

* @param block The Effect computation to execute

371

* @param onError Function to handle error case

372

* @param onSuccess Function to handle success case

373

*/

374

suspend fun <Error, A, B> Effect<Error, A>.fold(

375

onError: (Error) -> B,

376

onSuccess: (A) -> B

377

): B

378

379

/**

380

* Map over the success value of an Effect

381

* @param transform Function to transform the success value

382

*/

383

suspend fun <Error, A, B> Effect<Error, A>.map(

384

transform: suspend (A) -> B

385

): Effect<Error, B>

386

387

/**

388

* Bind two Effects together

389

* @param transform Function that takes success value and returns new Effect

390

*/

391

suspend fun <Error, A, B> Effect<Error, A>.flatMap(

392

transform: suspend (A) -> Effect<Error, B>

393

): Effect<Error, B>

394

395

/**

396

* Handle errors in an Effect computation

397

* @param recover Function to recover from errors

398

*/

399

suspend fun <Error, A, E2> Effect<Error, A>.handleErrorWith(

400

recover: suspend (Error) -> Effect<E2, A>

401

): Effect<E2, A>

402

403

/**

404

* Get the result of an Effect, returning null if it fails

405

* @return The success value or null if error was raised

406

*/

407

suspend fun <Error, A> Effect<Error, A>.getOrNull(): A?

408

409

/**

410

* Merge Effect where Error and A are the same type

411

* @return The value, whether it was error or success

412

*/

413

suspend fun <A> Effect<A, A>.merge(): A

414

415

/**

416

* Execute multiple Effects in parallel, accumulating results

417

* @param effects Collection of Effects to execute

418

* @return Effect containing list of all results

419

*/

420

suspend fun <Error, A> Collection<Effect<Error, A>>.parZip(): Effect<Error, List<A>>

421

```

422

423

**Usage Examples:**

424

425

```kotlin

426

import arrow.core.raise.*

427

import arrow.core.*

428

import kotlinx.coroutines.*

429

430

// Suspended Effect computation

431

suspend fun fetchUserProfile(userId: String): Effect<String, UserProfile> = effect {

432

val user = fetchUser(userId) ?: raise("User not found: $userId")

433

val profile = fetchProfile(user.id) ?: raise("Profile not found for user: $userId")

434

435

UserProfile(user.name, profile.bio, profile.avatar)

436

}

437

438

// Using Effect with error handling

439

suspend fun handleUserRequest(userId: String) {

440

fetchUserProfile(userId).fold(

441

onError = { error ->

442

println("Failed to fetch profile: $error")

443

respondWithError(error)

444

},

445

onSuccess = { profile ->

446

println("Successfully fetched profile for ${profile.name}")

447

respondWithProfile(profile)

448

}

449

)

450

}

451

452

// Chaining Effects

453

suspend fun processUserData(userId: String): Effect<String, ProcessedData> = effect {

454

val profile = fetchUserProfile(userId).bind()

455

val settings = fetchUserSettings(userId).bind()

456

val preferences = fetchUserPreferences(userId).bind()

457

458

ProcessedData(profile, settings, preferences)

459

}

460

461

// Parallel execution

462

suspend fun fetchAllData(userIds: List<String>): Effect<String, List<UserProfile>> = effect {

463

userIds.map { userId ->

464

fetchUserProfile(userId)

465

}.parZip().bind()

466

}

467

468

// Error recovery

469

suspend fun fetchWithFallback(userId: String): Effect<String, UserProfile> = effect {

470

fetchUserProfile(userId)

471

.handleErrorWith { error ->

472

if (error.contains("not found")) {

473

effect { getDefaultProfile() }

474

} else {

475

effect { raise(error) }

476

}

477

}

478

.bind()

479

}

480

```

481

482

### RaiseAccumulate System

483

484

Advanced error accumulation capabilities for collecting multiple errors while continuing computation.

485

486

```kotlin { .api }

487

/**

488

* Raise context for accumulating errors with NonEmptyList

489

* @param Error The type of errors to accumulate

490

*/

491

class RaiseAccumulate<Error> : Raise<NonEmptyList<Error>> {

492

/**

493

* Map over a collection, accumulating errors

494

* @param transform Function that can raise errors

495

* @return Collection of results or accumulated errors

496

*/

497

fun <A, B> Iterable<A>.mapOrAccumulate(

498

transform: Raise<Error>.(A) -> B

499

): List<B>

500

501

/**

502

* Zip multiple values, accumulating errors

503

* @param action Function combining all values

504

* @return Combined result or accumulated errors

505

*/

506

fun <A, B, C> zipOrAccumulate(

507

first: Raise<Error>.() -> A,

508

second: Raise<Error>.() -> B,

509

action: (A, B) -> C

510

): C

511

512

// Additional zipOrAccumulate overloads for 3-10 parameters...

513

}

514

515

/**

516

* Execute computation with error accumulation

517

* @param block Computation that can accumulate errors

518

* @return Either with accumulated errors or success

519

*/

520

fun <Error, A> mapOrAccumulate(

521

block: RaiseAccumulate<Error>.() -> A

522

): Either<NonEmptyList<Error>, A>

523

```

524

525

**Usage Examples:**

526

527

```kotlin

528

import arrow.core.raise.*

529

import arrow.core.*

530

531

data class ValidationError(val field: String, val message: String)

532

533

fun validateUser(userData: UserData): Either<NonEmptyList<ValidationError>, ValidUser> =

534

mapOrAccumulate {

535

zipOrAccumulate(

536

{ validateName(userData.name) },

537

{ validateEmail(userData.email) },

538

{ validateAge(userData.age) }

539

) { name, email, age ->

540

ValidUser(name, email, age)

541

}

542

}

543

544

fun validateName(name: String): Raise<ValidationError>.() -> String = {

545

if (name.isBlank()) raise(ValidationError("name", "Name cannot be blank"))

546

if (name.length < 2) raise(ValidationError("name", "Name too short"))

547

name

548

}

549

550

// Processing multiple items with accumulation

551

fun validateAllUsers(users: List<UserData>): Either<NonEmptyList<ValidationError>, List<ValidUser>> =

552

mapOrAccumulate {

553

users.mapOrAccumulate { userData ->

554

validateUser(userData).bind()

555

}

556

}

557

```

558

559

### Utility Functions

560

561

Additional utility functions for advanced error handling patterns.

562

563

```kotlin { .api }

564

/**

565

* Ensure a condition is true, otherwise raise an error

566

* @param condition Boolean condition to check

567

* @param raise Function to produce error if condition is false

568

*/

569

fun <Error> Raise<Error>.ensure(condition: Boolean, raise: () -> Error): Unit

570

571

/**

572

* Ensure a value is not null, otherwise raise an error

573

* @param value Nullable value to check

574

* @param raise Function to produce error if value is null

575

* @return Non-null value

576

*/

577

fun <Error, A : Any> Raise<Error>.ensureNotNull(value: A?, raise: () -> Error): A

578

579

/**

580

* Transform errors before raising them

581

* @param transform Function to transform errors

582

* @param block Computation that can raise transformed errors

583

*/

584

inline fun <Error, A, TransformedError> withError(

585

transform: (Error) -> TransformedError,

586

block: Raise<Error>.() -> A

587

): Raise<TransformedError>.() -> A

588

589

/**

590

* Catch exceptions and convert to raised errors

591

* @param transform Function to convert exception to error

592

* @param block Computation that might throw exceptions

593

* @return Result or raised error

594

*/

595

inline fun <Error, A> Raise<Error>.catch(

596

transform: (Throwable) -> Error,

597

block: () -> A

598

): A

599

```

600

601

### Annotations

602

603

```kotlin { .api }

604

/**

605

* DSL marker for Raise contexts

606

*/

607

@DslMarker

608

annotation class RaiseDSL

609

610

/**

611

* Marks potentially unsafe Raise operations

612

*/

613

annotation class DelicateRaiseApi

614

615

/**

616

* Marks experimental accumulation APIs

617

*/

618

annotation class ExperimentalRaiseAccumulateApi

619

620

/**

621

* Marks experimental tracing APIs

622

*/

623

annotation class ExperimentalTraceApi

624

```