or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotation-generation.mdbuilders.mdextensions.mdindex.mdmemoization.mdpretty-strings.mdserialization.mdstandalone-builders.mdtagged-unions.mdvalue-classes.md

tagged-unions.mddocs/

0

# Tagged Unions

1

2

AutoOneOf generates tagged union (also known as sum types or algebraic data types) implementations that can represent a value that is exactly one of several possible types.

3

4

## Basic Tagged Union

5

6

```java { .api }

7

@AutoOneOf(StringOrInteger.Kind.class)

8

public abstract class StringOrInteger {

9

public enum Kind {

10

STRING, INTEGER

11

}

12

13

public abstract Kind getKind();

14

15

public abstract String string();

16

public abstract int integer();

17

18

public static StringOrInteger ofString(String s) {

19

return AutoOneOf_StringOrInteger.string(s);

20

}

21

22

public static StringOrInteger ofInteger(int i) {

23

return AutoOneOf_StringOrInteger.integer(i);

24

}

25

}

26

```

27

28

## Usage Example

29

30

```java

31

StringOrInteger stringValue = StringOrInteger.ofString("hello");

32

StringOrInteger intValue = StringOrInteger.ofInteger(42);

33

34

// Pattern matching with switch

35

String result = processValue(stringValue);

36

37

public String processValue(StringOrInteger value) {

38

switch (value.getKind()) {

39

case STRING:

40

return "String: " + value.string();

41

case INTEGER:

42

return "Integer: " + value.integer();

43

}

44

throw new AssertionError("Unknown kind: " + value.getKind());

45

}

46

```

47

48

## Complex Tagged Union

49

50

```java { .api }

51

@AutoOneOf(Result.Kind.class)

52

public abstract class Result<T, E> {

53

public enum Kind {

54

SUCCESS, ERROR

55

}

56

57

public abstract Kind getKind();

58

59

public abstract T success();

60

public abstract E error();

61

62

public static <T, E> Result<T, E> success(T value) {

63

return AutoOneOf_Result.success(value);

64

}

65

66

public static <T, E> Result<T, E> error(E error) {

67

return AutoOneOf_Result.error(error);

68

}

69

}

70

```

71

72

Usage:

73

74

```java

75

Result<String, Exception> successResult = Result.success("Operation completed");

76

Result<String, Exception> errorResult = Result.error(new RuntimeException("Failed"));

77

78

// Processing results

79

public <T, E> void handleResult(Result<T, E> result) {

80

switch (result.getKind()) {

81

case SUCCESS:

82

System.out.println("Success: " + result.success());

83

break;

84

case ERROR:

85

System.err.println("Error: " + result.error().getMessage());

86

break;

87

}

88

}

89

```

90

91

## Multiple Type Union

92

93

```java { .api }

94

@AutoOneOf(Value.Kind.class)

95

public abstract class Value {

96

public enum Kind {

97

STRING, INTEGER, DOUBLE, BOOLEAN, LIST

98

}

99

100

public abstract Kind getKind();

101

102

public abstract String string();

103

public abstract int integer();

104

public abstract double doubleValue();

105

public abstract boolean booleanValue();

106

public abstract List<Value> list();

107

108

public static Value ofString(String s) {

109

return AutoOneOf_Value.string(s);

110

}

111

112

public static Value ofInteger(int i) {

113

return AutoOneOf_Value.integer(i);

114

}

115

116

public static Value ofDouble(double d) {

117

return AutoOneOf_Value.doubleValue(d);

118

}

119

120

public static Value ofBoolean(boolean b) {

121

return AutoOneOf_Value.booleanValue(b);

122

}

123

124

public static Value ofList(List<Value> list) {

125

return AutoOneOf_Value.list(list);

126

}

127

}

128

```

129

130

Usage:

131

132

```java

133

Value stringVal = Value.ofString("text");

134

Value numberVal = Value.ofInteger(123);

135

Value listVal = Value.ofList(Arrays.asList(stringVal, numberVal));

136

137

// JSON-like processing

138

public Object toJson(Value value) {

139

switch (value.getKind()) {

140

case STRING:

141

return value.string();

142

case INTEGER:

143

return value.integer();

144

case DOUBLE:

145

return value.doubleValue();

146

case BOOLEAN:

147

return value.booleanValue();

148

case LIST:

149

return value.list().stream()

150

.map(this::toJson)

151

.collect(Collectors.toList());

152

}

153

throw new AssertionError();

154

}

155

```

156

157

## Optional-like Union

158

159

```java { .api }

160

@AutoOneOf(Optional.Kind.class)

161

public abstract class Optional<T> {

162

public enum Kind {

163

PRESENT, ABSENT

164

}

165

166

public abstract Kind getKind();

167

168

public abstract T present();

169

public abstract Void absent(); // Void for empty case

170

171

public static <T> Optional<T> of(T value) {

172

return AutoOneOf_Optional.present(value);

173

}

174

175

public static <T> Optional<T> empty() {

176

return AutoOneOf_Optional.absent(null);

177

}

178

179

// Convenience methods

180

public boolean isPresent() {

181

return getKind() == Kind.PRESENT;

182

}

183

184

public boolean isEmpty() {

185

return getKind() == Kind.ABSENT;

186

}

187

188

public T orElse(T defaultValue) {

189

return isPresent() ? present() : defaultValue;

190

}

191

}

192

```

193

194

Usage:

195

196

```java

197

Optional<String> present = Optional.of("value");

198

Optional<String> empty = Optional.empty();

199

200

String result1 = present.orElse("default"); // "value"

201

String result2 = empty.orElse("default"); // "default"

202

```

203

204

## Recursive Tagged Unions

205

206

Tagged unions can reference themselves for tree-like structures:

207

208

```java { .api }

209

@AutoOneOf(Expression.Kind.class)

210

public abstract class Expression {

211

public enum Kind {

212

NUMBER, VARIABLE, BINARY_OP

213

}

214

215

public abstract Kind getKind();

216

217

public abstract double number();

218

public abstract String variable();

219

public abstract BinaryOp binaryOp();

220

221

public static Expression number(double value) {

222

return AutoOneOf_Expression.number(value);

223

}

224

225

public static Expression variable(String name) {

226

return AutoOneOf_Expression.variable(name);

227

}

228

229

public static Expression binaryOp(String operator, Expression left, Expression right) {

230

return AutoOneOf_Expression.binaryOp(BinaryOp.create(operator, left, right));

231

}

232

233

@AutoValue

234

public abstract static class BinaryOp {

235

public abstract String operator();

236

public abstract Expression left();

237

public abstract Expression right();

238

239

public static BinaryOp create(String operator, Expression left, Expression right) {

240

return new AutoValue_Expression_BinaryOp(operator, left, right);

241

}

242

}

243

}

244

```

245

246

Usage:

247

248

```java

249

// Build expression: (x + 2) * 3

250

Expression x = Expression.variable("x");

251

Expression two = Expression.number(2);

252

Expression three = Expression.number(3);

253

Expression xPlusTwo = Expression.binaryOp("+", x, two);

254

Expression result = Expression.binaryOp("*", xPlusTwo, three);

255

256

// Evaluate expression

257

public double evaluate(Expression expr, Map<String, Double> variables) {

258

switch (expr.getKind()) {

259

case NUMBER:

260

return expr.number();

261

case VARIABLE:

262

return variables.get(expr.variable());

263

case BINARY_OP:

264

BinaryOp op = expr.binaryOp();

265

double left = evaluate(op.left(), variables);

266

double right = evaluate(op.right(), variables);

267

switch (op.operator()) {

268

case "+": return left + right;

269

case "*": return left * right;

270

// ... other operators

271

}

272

throw new IllegalArgumentException("Unknown operator: " + op.operator());

273

}

274

throw new AssertionError();

275

}

276

```

277

278

## Nullable Variants

279

280

Tagged unions can have nullable variants:

281

282

```java { .api }

283

@AutoOneOf(NullableValue.Kind.class)

284

public abstract class NullableValue<T> {

285

public enum Kind {

286

VALUE, NULL

287

}

288

289

public abstract Kind getKind();

290

291

@Nullable

292

public abstract T value();

293

public abstract Void nullValue();

294

295

public static <T> NullableValue<T> of(@Nullable T value) {

296

return value != null

297

? AutoOneOf_NullableValue.value(value)

298

: AutoOneOf_NullableValue.nullValue(null);

299

}

300

301

public static <T> NullableValue<T> nullValue() {

302

return AutoOneOf_NullableValue.nullValue(null);

303

}

304

}

305

```

306

307

## Error Handling in Tagged Unions

308

309

Use tagged unions for comprehensive error handling:

310

311

```java { .api }

312

@AutoOneOf(ApiResponse.Kind.class)

313

public abstract class ApiResponse<T> {

314

public enum Kind {

315

SUCCESS, CLIENT_ERROR, SERVER_ERROR, NETWORK_ERROR

316

}

317

318

public abstract Kind getKind();

319

320

public abstract T success();

321

public abstract ClientError clientError();

322

public abstract ServerError serverError();

323

public abstract NetworkError networkError();

324

325

public static <T> ApiResponse<T> success(T data) {

326

return AutoOneOf_ApiResponse.success(data);

327

}

328

329

public static <T> ApiResponse<T> clientError(int code, String message) {

330

return AutoOneOf_ApiResponse.clientError(ClientError.create(code, message));

331

}

332

333

public static <T> ApiResponse<T> serverError(int code, String message) {

334

return AutoOneOf_ApiResponse.serverError(ServerError.create(code, message));

335

}

336

337

public static <T> ApiResponse<T> networkError(Exception cause) {

338

return AutoOneOf_ApiResponse.networkError(NetworkError.create(cause));

339

}

340

341

@AutoValue

342

public abstract static class ClientError {

343

public abstract int code();

344

public abstract String message();

345

346

static ClientError create(int code, String message) {

347

return new AutoValue_ApiResponse_ClientError(code, message);

348

}

349

}

350

351

@AutoValue

352

public abstract static class ServerError {

353

public abstract int code();

354

public abstract String message();

355

356

static ServerError create(int code, String message) {

357

return new AutoValue_ApiResponse_ServerError(code, message);

358

}

359

}

360

361

@AutoValue

362

public abstract static class NetworkError {

363

public abstract Exception cause();

364

365

static NetworkError create(Exception cause) {

366

return new AutoValue_ApiResponse_NetworkError(cause);

367

}

368

}

369

}

370

```

371

372

Usage:

373

374

```java

375

ApiResponse<User> response = apiCall();

376

377

switch (response.getKind()) {

378

case SUCCESS:

379

User user = response.success();

380

displayUser(user);

381

break;

382

case CLIENT_ERROR:

383

ClientError error = response.clientError();

384

showError("Client error " + error.code() + ": " + error.message());

385

break;

386

case SERVER_ERROR:

387

ServerError error = response.serverError();

388

showError("Server error " + error.code() + ": " + error.message());

389

break;

390

case NETWORK_ERROR:

391

NetworkError error = response.networkError();

392

showError("Network error: " + error.cause().getMessage());

393

retry();

394

break;

395

}

396

```

397

398

## Builder Integration

399

400

Tagged unions work with builders for complex construction:

401

402

```java { .api }

403

@AutoOneOf(Message.Kind.class)

404

public abstract class Message {

405

public enum Kind {

406

TEXT, IMAGE, FILE

407

}

408

409

public abstract Kind getKind();

410

411

public abstract TextMessage text();

412

public abstract ImageMessage image();

413

public abstract FileMessage file();

414

415

@AutoValue

416

public abstract static class TextMessage {

417

public abstract String content();

418

public abstract Optional<String> format();

419

420

public static Builder builder() {

421

return new AutoValue_Message_TextMessage.Builder();

422

}

423

424

@AutoValue.Builder

425

public abstract static class Builder {

426

public abstract Builder content(String content);

427

public abstract Builder format(String format);

428

public abstract TextMessage build();

429

}

430

}

431

432

// Similar for ImageMessage and FileMessage...

433

434

public static Message text(String content) {

435

return AutoOneOf_Message.text(

436

TextMessage.builder().content(content).build());

437

}

438

}

439

```

440

441

## Performance Considerations

442

443

- Generated classes use efficient switch-based dispatching

444

- No boxing/unboxing for primitive types

445

- Generated equals() and hashCode() are optimized

446

- Kind enum provides O(1) type checking

447

- No reflection used at runtime