or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

collections.mderror-handling.mdfunctional-utilities.mdinclusive-or.mdindex.mdoptional-values.mdraise-dsl.md

inclusive-or.mddocs/

0

# Inclusive OR with Ior

1

2

Represents values that can be either one type, another type, or both simultaneously. Ior (Inclusive OR) is useful for computations that can produce partial results alongside errors or warnings, allowing you to accumulate information from both success and failure paths.

3

4

## Capabilities

5

6

### Ior Construction

7

8

Create Ior instances representing left values, right values, or both.

9

10

```kotlin { .api }

11

/**

12

* Ior containing only a left value (typically error/warning)

13

*/

14

data class Left<out A>(val value: A) : Ior<A, Nothing>

15

16

/**

17

* Ior containing only a right value (typically success)

18

*/

19

data class Right<out B>(val value: B) : Ior<Nothing, B>

20

21

/**

22

* Ior containing both left and right values

23

*/

24

data class Both<out A, out B>(val left: A, val right: B) : Ior<A, B>

25

```

26

27

### Ior Extension Functions

28

29

Convenience functions for creating Ior instances.

30

31

```kotlin { .api }

32

/**

33

* Wrap any value in Ior.Left

34

*/

35

fun <A> A.leftIor(): Ior<A, Nothing>

36

37

/**

38

* Wrap any value in Ior.Right

39

*/

40

fun <A> A.rightIor(): Ior<Nothing, A>

41

42

/**

43

* Create Ior.Both from two values

44

*/

45

fun <A, B> bothIor(left: A, right: B): Ior<A, B>

46

```

47

48

**Usage Examples:**

49

50

```kotlin

51

// Direct construction

52

val error: Ior<String, Int> = Ior.Left("Error occurred")

53

val success: Ior<String, Int> = Ior.Right(42)

54

val partial: Ior<String, Int> = Ior.Both("Warning: partial result", 42)

55

56

// Using extension functions

57

val warning = "Deprecated API".leftIor()

58

val result = 100.rightIor()

59

val combined = bothIor("Processing with warnings", listOf(1, 2, 3))

60

```

61

62

### Ior Type Guards

63

64

Check the state of Ior instances with type-safe guards.

65

66

```kotlin { .api }

67

/**

68

* Check if Ior contains only left value

69

* @return true if Left, false otherwise

70

*/

71

fun <A, B> Ior<A, B>.isLeft(): Boolean

72

73

/**

74

* Check if Ior contains only right value

75

* @return true if Right, false otherwise

76

*/

77

fun <A, B> Ior<A, B>.isRight(): Boolean

78

79

/**

80

* Check if Ior contains both left and right values

81

* @return true if Both, false otherwise

82

*/

83

fun <A, B> Ior<A, B>.isBoth(): Boolean

84

```

85

86

### Ior Pattern Matching

87

88

Safely extract values or perform operations based on Ior state.

89

90

```kotlin { .api }

91

/**

92

* Pattern match on Ior, providing handlers for all three cases

93

*/

94

fun <A, B, C> Ior<A, B>.fold(

95

ifLeft: (A) -> C,

96

ifRight: (B) -> C,

97

ifBoth: (A, B) -> C

98

): C

99

100

/**

101

* Extract values into a pair of optionals

102

*/

103

fun <A, B> Ior<A, B>.pad(): Pair<A?, B?>

104

105

/**

106

* Unwrap into Either<Either<A, B>, Pair<A, B>>

107

*/

108

fun <A, B> Ior<A, B>.unwrap(): Either<Either<A, B>, Pair<A, B>>

109

```

110

111

**Usage Examples:**

112

113

```kotlin

114

val result: Ior<String, Int> = Ior.Both("Warning", 42)

115

116

// Pattern matching

117

val message = result.fold(

118

ifLeft = { error -> "Error: $error" },

119

ifRight = { value -> "Success: $value" },

120

ifBoth = { warning, value -> "Warning: $warning, Result: $value" }

121

)

122

123

// Extract as optional pair

124

val (maybeWarning, maybeResult) = result.pad() // ("Warning", 42)

125

126

// Check state

127

val hasBoth = result.isBoth() // true

128

val hasWarning = result.isLeft() || result.isBoth() // true

129

```

130

131

### Ior Transformations

132

133

Transform Ior values while preserving the container structure.

134

135

```kotlin { .api }

136

/**

137

* Transform the right value if present

138

*/

139

fun <A, B, C> Ior<A, B>.map(f: (B) -> C): Ior<A, C>

140

141

/**

142

* Transform the left value if present

143

*/

144

fun <A, B, C> Ior<A, B>.mapLeft(f: (A) -> C): Ior<C, B>

145

146

/**

147

* Transform both values if both are present

148

*/

149

fun <A, B, C, D> Ior<A, B>.bimap(

150

leftMap: (A) -> C,

151

rightMap: (B) -> D

152

): Ior<C, D>

153

154

/**

155

* Swap left and right positions

156

*/

157

fun <A, B> Ior<A, B>.swap(): Ior<B, A>

158

```

159

160

**Usage Examples:**

161

162

```kotlin

163

val processing: Ior<List<String>, List<Int>> = Ior.Both(

164

listOf("Warning: deprecated field"),

165

listOf(1, 2, 3)

166

)

167

168

// Transform right values

169

val doubled = processing.map { numbers -> numbers.map { it * 2 } }

170

// Result: Ior.Both(["Warning: deprecated field"], [2, 4, 6])

171

172

// Transform left values

173

val formatted = processing.mapLeft { warnings ->

174

warnings.map { "⚠️ $it" }

175

}

176

177

// Transform both sides

178

val processed = processing.bimap(

179

leftMap = { warnings -> warnings.size },

180

rightMap = { numbers -> numbers.sum() }

181

)

182

// Result: Ior.Both(1, 6)

183

```

184

185

### Ior Monadic Operations

186

187

Chain Ior operations with proper left value combination.

188

189

```kotlin { .api }

190

/**

191

* Monadic bind with left value combination

192

*/

193

fun <A, B, C> Ior<A, B>.flatMap(

194

combine: (A, A) -> A,

195

f: (B) -> Ior<A, C>

196

): Ior<A, C>

197

```

198

199

**Usage Examples:**

200

201

```kotlin

202

fun processStep1(input: String): Ior<List<String>, Int> {

203

val warnings = mutableListOf<String>()

204

val result = input.length

205

206

if (input.contains("deprecated")) {

207

warnings.add("Using deprecated input format")

208

}

209

210

return if (warnings.isEmpty()) {

211

Ior.Right(result)

212

} else {

213

Ior.Both(warnings, result)

214

}

215

}

216

217

fun processStep2(value: Int): Ior<List<String>, String> {

218

val warnings = mutableListOf<String>()

219

val result = "Processed: $value"

220

221

if (value > 100) {

222

warnings.add("Large value detected")

223

}

224

225

return if (warnings.isEmpty()) {

226

Ior.Right(result)

227

} else {

228

Ior.Both(warnings, result)

229

}

230

}

231

232

// Chain operations, combining warnings

233

val pipeline = processStep1("deprecated_input_with_long_name")

234

.flatMap(

235

combine = { w1, w2 -> w1 + w2 } // Combine warning lists

236

) { value -> processStep2(value) }

237

238

// Result: Ior.Both(

239

// ["Using deprecated input format", "Large value detected"],

240

// "Processed: 26"

241

// )

242

```

243

244

### Ior Conversions

245

246

Convert Ior to other Arrow types.

247

248

```kotlin { .api }

249

/**

250

* Convert Ior to Either, losing Both case (Right wins)

251

*/

252

fun <A, B> Ior<A, B>.toEither(): Either<A, B>

253

254

/**

255

* Convert Ior to Option, keeping only Right values

256

*/

257

fun <A, B> Ior<A, B>.toOption(): Option<B>

258

259

/**

260

* Convert Ior to nullable, keeping only Right values

261

*/

262

fun <A, B> Ior<A, B>.orNull(): B?

263

```

264

265

**Usage Examples:**

266

267

```kotlin

268

val both: Ior<String, Int> = Ior.Both("warning", 42)

269

val left: Ior<String, Int> = Ior.Left("error")

270

val right: Ior<String, Int> = Ior.Right(100)

271

272

// Convert to Either (Right wins in Both case)

273

val eitherFromBoth = both.toEither() // Either.Right(42)

274

val eitherFromLeft = left.toEither() // Either.Left("error")

275

val eitherFromRight = right.toEither() // Either.Right(100)

276

277

// Convert to Option (only Right values)

278

val optionFromBoth = both.toOption() // Some(42)

279

val optionFromLeft = left.toOption() // None

280

val optionFromRight = right.toOption() // Some(100)

281

```

282

283

### Ior Combination

284

285

Combine multiple Ior values with left value accumulation.

286

287

```kotlin { .api }

288

/**

289

* Zip two Ior values with left combination function

290

*/

291

fun <A, B, C, D> Ior<A, B>.zip(

292

combine: (A, A) -> A,

293

other: Ior<A, C>,

294

f: (B, C) -> D

295

): Ior<A, D>

296

297

/**

298

* Apply function if both sides have right values

299

*/

300

fun <A, B, C> Ior<A, B>.ap(

301

combine: (A, A) -> A,

302

f: Ior<A, (B) -> C>

303

): Ior<A, C>

304

```

305

306

**Usage Examples:**

307

308

```kotlin

309

val result1: Ior<List<String>, Int> = Ior.Both(listOf("warning1"), 10)

310

val result2: Ior<List<String>, Int> = Ior.Both(listOf("warning2"), 20)

311

312

// Combine with warning accumulation

313

val combined = result1.zip(

314

combine = { w1, w2 -> w1 + w2 }, // Combine warning lists

315

other = result2

316

) { a, b -> a + b }

317

318

// Result: Ior.Both(["warning1", "warning2"], 30)

319

```

320

321

## Ior Use Cases

322

323

### Logging and Computation

324

325

Accumulate logs/warnings while computing results.

326

327

```kotlin

328

fun processData(data: List<String>): Ior<List<String>, List<Int>> {

329

val warnings = mutableListOf<String>()

330

val results = mutableListOf<Int>()

331

332

for (item in data) {

333

when {

334

item.isBlank() -> warnings.add("Empty item skipped")

335

item.startsWith("old_") -> {

336

warnings.add("Legacy format detected: $item")

337

results.add(item.removePrefix("old_").length)

338

}

339

else -> results.add(item.length)

340

}

341

}

342

343

return when {

344

warnings.isEmpty() -> Ior.Right(results)

345

results.isEmpty() -> Ior.Left(warnings)

346

else -> Ior.Both(warnings, results)

347

}

348

}

349

```

350

351

### Partial Validation

352

353

Validate data while collecting all validation errors.

354

355

```kotlin

356

data class ValidationResult<A>(val warnings: List<String>, val value: A)

357

358

fun validateUser(input: UserInput): Ior<List<String>, User> {

359

val warnings = mutableListOf<String>()

360

361

val name = if (input.name.isNotBlank()) {

362

input.name

363

} else {

364

warnings.add("Name should not be empty")

365

"Unknown"

366

}

367

368

val email = if (input.email.contains("@")) {

369

input.email

370

} else {

371

warnings.add("Invalid email format")

372

"unknown@example.com"

373

}

374

375

val user = User(name, email)

376

377

return if (warnings.isEmpty()) {

378

Ior.Right(user)

379

} else {

380

Ior.Both(warnings, user)

381

}

382

}

383

```

384

385

## Types

386

387

### IorNel Type Alias

388

389

```kotlin { .api }

390

/**

391

* Ior with NonEmptyList for error accumulation

392

*/

393

typealias IorNel<E, A> = Ior<NonEmptyList<E>, A>

394

```