or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

context-management.mdcore-operations.mderror-handling.mdindex.mdtype-system.md

type-system.mddocs/

0

# Type System

1

2

Comprehensive type system with DataType constants for all supported data types, type checking utilities, and type coercion functions. The type system enables optional type safety, validation, and proper handling of Python data types within rule expressions.

3

4

## Capabilities

5

6

### DataType Constants

7

8

The DataType class provides constants for all supported data types with type checking and compatibility methods.

9

10

```python { .api }

11

class DataType:

12

"""Collection of constants representing supported data types."""

13

14

# Scalar Types

15

BOOLEAN: DataType # bool values

16

BYTES: DataType # bytes objects

17

DATETIME: DataType # datetime.datetime objects

18

FLOAT: DataType # decimal.Decimal, float, int (non-bool)

19

NULL: DataType # None values

20

STRING: DataType # str values

21

TIMEDELTA: DataType # datetime.timedelta objects

22

23

# Compound Types

24

ARRAY: DataType # tuple, list, range objects

25

MAPPING: DataType # dict, OrderedDict objects

26

SET: DataType # set objects

27

28

# Special Types

29

FUNCTION: DataType # callable objects

30

UNDEFINED: DataType # undefined/unknown types

31

```

32

33

**Usage Example:**

34

35

```python

36

import rule_engine

37

38

# Using type constants for validation

39

print(rule_engine.DataType.STRING.name) # "STRING"

40

print(rule_engine.DataType.FLOAT.python_type) # <class 'decimal.Decimal'>

41

42

# Check if a type is scalar or compound

43

print(rule_engine.DataType.STRING.is_scalar) # True

44

print(rule_engine.DataType.ARRAY.is_compound) # True

45

print(rule_engine.DataType.MAPPING.is_iterable) # True

46

```

47

48

### Type Resolution from Names

49

50

Get DataType constants from their string names.

51

52

```python { .api }

53

@classmethod

54

def from_name(cls, name: str) -> DataType:

55

"""

56

Get the data type from its name.

57

58

Args:

59

name (str): The name of the data type to retrieve

60

61

Returns:

62

DataType: The corresponding data type constant

63

64

Raises:

65

TypeError: If name is not a string

66

ValueError: If name doesn't map to a valid data type

67

"""

68

```

69

70

**Usage Example:**

71

72

```python

73

import rule_engine

74

75

# Get types by name

76

string_type = rule_engine.DataType.from_name('STRING')

77

float_type = rule_engine.DataType.from_name('FLOAT')

78

array_type = rule_engine.DataType.from_name('ARRAY')

79

80

print(string_type == rule_engine.DataType.STRING) # True

81

82

# Error handling

83

try:

84

invalid_type = rule_engine.DataType.from_name('INVALID')

85

except ValueError as e:

86

print(f"Invalid type name: {e}")

87

```

88

89

### Type Resolution from Python Types

90

91

Get DataType constants from Python types and type hints.

92

93

```python { .api }

94

@classmethod

95

def from_type(cls, python_type) -> DataType:

96

"""

97

Get the data type constant for a Python type or type hint.

98

99

Args:

100

python_type: Native Python type or type hint

101

102

Returns:

103

DataType: The corresponding data type constant

104

105

Raises:

106

TypeError: If argument is not a type or type hint

107

ValueError: If type cannot be mapped to a supported data type

108

"""

109

```

110

111

**Usage Example:**

112

113

```python

114

import rule_engine

115

from typing import List, Dict

116

import datetime

117

118

# Basic Python types

119

print(rule_engine.DataType.from_type(str)) # DataType.STRING

120

print(rule_engine.DataType.from_type(int)) # DataType.FLOAT

121

print(rule_engine.DataType.from_type(bool)) # DataType.BOOLEAN

122

print(rule_engine.DataType.from_type(list)) # DataType.ARRAY

123

print(rule_engine.DataType.from_type(dict)) # DataType.MAPPING

124

125

# Datetime types

126

print(rule_engine.DataType.from_type(datetime.datetime)) # DataType.DATETIME

127

print(rule_engine.DataType.from_type(datetime.timedelta)) # DataType.TIMEDELTA

128

129

# Type hints (Python 3.6+)

130

list_type = rule_engine.DataType.from_type(List[str])

131

print(list_type.name) # "ARRAY"

132

if hasattr(list_type, 'value_type'):

133

print(list_type.value_type.name) # "STRING"

134

135

dict_type = rule_engine.DataType.from_type(Dict[str, int])

136

print(dict_type.name) # "MAPPING"

137

```

138

139

### Type Resolution from Values

140

141

Get DataType constants from Python values with automatic type detection.

142

143

```python { .api }

144

@classmethod

145

def from_value(cls, python_value) -> DataType:

146

"""

147

Get the data type constant for a Python value.

148

149

Args:

150

python_value: Native Python value to analyze

151

152

Returns:

153

DataType: The corresponding data type constant

154

155

Raises:

156

TypeError: If value cannot be mapped to a supported data type

157

"""

158

```

159

160

**Usage Example:**

161

162

```python

163

import rule_engine

164

import datetime

165

from collections import OrderedDict

166

167

# Scalar values

168

print(rule_engine.DataType.from_value("hello")) # DataType.STRING

169

print(rule_engine.DataType.from_value(42)) # DataType.FLOAT

170

print(rule_engine.DataType.from_value(True)) # DataType.BOOLEAN

171

print(rule_engine.DataType.from_value(None)) # DataType.NULL

172

print(rule_engine.DataType.from_value(b"bytes")) # DataType.BYTES

173

174

# Datetime values

175

now = datetime.datetime.now()

176

delta = datetime.timedelta(days=1)

177

print(rule_engine.DataType.from_value(now)) # DataType.DATETIME

178

print(rule_engine.DataType.from_value(delta)) # DataType.TIMEDELTA

179

180

# Collections

181

print(rule_engine.DataType.from_value([1, 2, 3])) # DataType.ARRAY

182

print(rule_engine.DataType.from_value({'a': 1, 'b': 2})) # DataType.MAPPING

183

print(rule_engine.DataType.from_value({1, 2, 3})) # DataType.SET

184

185

# Typed collections

186

array_type = rule_engine.DataType.from_value([1, 2, 3])

187

print(array_type.value_type.name) # "FLOAT" (all integers)

188

189

mixed_array = rule_engine.DataType.from_value([1, "hello", None])

190

print(mixed_array.value_type.name) # "UNDEFINED" (mixed types)

191

```

192

193

### Type Compatibility Checking

194

195

Check if two data types are compatible without conversion.

196

197

```python { .api }

198

@classmethod

199

def is_compatible(cls, dt1: DataType, dt2: DataType) -> bool:

200

"""

201

Check if two data types are compatible without conversion.

202

203

Args:

204

dt1: First data type to compare

205

dt2: Second data type to compare

206

207

Returns:

208

bool: True if types are compatible

209

210

Raises:

211

TypeError: If arguments are not data type definitions

212

"""

213

```

214

215

**Usage Example:**

216

217

```python

218

import rule_engine

219

220

# Scalar type compatibility

221

string_type = rule_engine.DataType.STRING

222

float_type = rule_engine.DataType.FLOAT

223

undefined_type = rule_engine.DataType.UNDEFINED

224

225

print(rule_engine.DataType.is_compatible(string_type, string_type)) # True

226

print(rule_engine.DataType.is_compatible(string_type, float_type)) # False

227

print(rule_engine.DataType.is_compatible(string_type, undefined_type)) # True (undefined is always compatible)

228

229

# Compound type compatibility

230

array_str = rule_engine.DataType.ARRAY(rule_engine.DataType.STRING)

231

array_float = rule_engine.DataType.ARRAY(rule_engine.DataType.FLOAT)

232

array_undefined = rule_engine.DataType.ARRAY(rule_engine.DataType.UNDEFINED)

233

234

print(rule_engine.DataType.is_compatible(array_str, array_str)) # True

235

print(rule_engine.DataType.is_compatible(array_str, array_float)) # False

236

print(rule_engine.DataType.is_compatible(array_str, array_undefined)) # True

237

```

238

239

### Type Definition Checking

240

241

Check if a value is a data type definition.

242

243

```python { .api }

244

@classmethod

245

def is_definition(cls, value) -> bool:

246

"""

247

Check if a value is a data type definition.

248

249

Args:

250

value: The value to check

251

252

Returns:

253

bool: True if value is a data type definition

254

"""

255

```

256

257

**Usage Example:**

258

259

```python

260

import rule_engine

261

262

# Check if values are type definitions

263

print(rule_engine.DataType.is_definition(rule_engine.DataType.STRING)) # True

264

print(rule_engine.DataType.is_definition("STRING")) # False

265

print(rule_engine.DataType.is_definition(42)) # False

266

267

# Useful for validation

268

def validate_type_resolver(type_map):

269

for symbol, dtype in type_map.items():

270

if not rule_engine.DataType.is_definition(dtype):

271

raise ValueError(f"Invalid type definition for {symbol}: {dtype}")

272

```

273

274

## Type Utility Functions

275

276

### Value Coercion

277

278

Convert Python values to rule engine compatible types.

279

280

```python { .api }

281

def coerce_value(value, verify_type: bool = True):

282

"""

283

Convert a Python value to a rule engine compatible type.

284

285

Args:

286

value: The value to convert

287

verify_type (bool): Whether to verify the converted type

288

289

Returns:

290

The converted value

291

292

Raises:

293

TypeError: If verify_type=True and type is incompatible

294

"""

295

```

296

297

**Usage Example:**

298

299

```python

300

import rule_engine

301

import datetime

302

from collections import OrderedDict

303

304

# Automatic coercion

305

coerced_list = rule_engine.coerce_value([1, 2, 3]) # Converts to tuple

306

print(type(coerced_list)) # <class 'tuple'>

307

308

coerced_date = rule_engine.coerce_value(datetime.date(2023, 1, 1)) # Converts to datetime

309

print(type(coerced_date)) # <class 'datetime.datetime'>

310

311

coerced_float = rule_engine.coerce_value(42) # Converts to Decimal

312

print(type(coerced_float)) # <class 'decimal.Decimal'>

313

314

# Nested structures

315

nested_data = {

316

'numbers': [1, 2, 3],

317

'info': {'name': 'test'}

318

}

319

coerced_nested = rule_engine.coerce_value(nested_data)

320

print(type(coerced_nested)) # <class 'collections.OrderedDict'>

321

```

322

323

### Numeric Type Checking

324

325

Utility functions for checking numeric properties of values.

326

327

```python { .api }

328

def is_numeric(value) -> bool:

329

"""Check if value is numeric (int, float, Decimal, but not bool)."""

330

331

def is_real_number(value) -> bool:

332

"""Check if value is a real number (finite, not NaN)."""

333

334

def is_integer_number(value) -> bool:

335

"""Check if value is an integer number (whole number)."""

336

337

def is_natural_number(value) -> bool:

338

"""Check if value is a natural number (non-negative integer)."""

339

```

340

341

**Usage Example:**

342

343

```python

344

import rule_engine

345

import decimal

346

import math

347

348

# Numeric checks

349

print(rule_engine.is_numeric(42)) # True

350

print(rule_engine.is_numeric(3.14)) # True

351

print(rule_engine.is_numeric(True)) # False (bool excluded)

352

print(rule_engine.is_numeric("42")) # False

353

354

# Real number checks

355

print(rule_engine.is_real_number(42)) # True

356

print(rule_engine.is_real_number(float('inf'))) # False

357

print(rule_engine.is_real_number(float('nan'))) # False

358

359

# Integer checks

360

print(rule_engine.is_integer_number(42)) # True

361

print(rule_engine.is_integer_number(3.0)) # True (whole number)

362

print(rule_engine.is_integer_number(3.14)) # False

363

364

# Natural number checks

365

print(rule_engine.is_natural_number(42)) # True

366

print(rule_engine.is_natural_number(0)) # True

367

print(rule_engine.is_natural_number(-5)) # False

368

```

369

370

### Iterable Member Type Analysis

371

372

Analyze the types of members in iterable collections.

373

374

```python { .api }

375

def iterable_member_value_type(python_value) -> DataType:

376

"""

377

Get the data type of iterable members if consistent.

378

379

Args:

380

python_value: Iterable to analyze

381

382

Returns:

383

DataType: The member type (UNDEFINED if mixed types)

384

"""

385

```

386

387

**Usage Example:**

388

389

```python

390

import rule_engine

391

392

# Homogeneous collections

393

numbers = [1, 2, 3, 4]

394

member_type = rule_engine.iterable_member_value_type(numbers)

395

print(member_type.name) # "FLOAT"

396

397

strings = ["a", "b", "c"]

398

member_type = rule_engine.iterable_member_value_type(strings)

399

print(member_type.name) # "STRING"

400

401

# Heterogeneous collections

402

mixed = [1, "hello", True]

403

member_type = rule_engine.iterable_member_value_type(mixed)

404

print(member_type.name) # "UNDEFINED"

405

406

# Nullable collections (None values allowed)

407

nullable = [1, 2, None, 3]

408

member_type = rule_engine.iterable_member_value_type(nullable)

409

print(member_type.name) # "FLOAT" (None is treated specially)

410

```

411

412

## Compound Type Definitions

413

414

### Array Types

415

416

Define array types with specific member types.

417

418

```python

419

import rule_engine

420

421

# Create typed array definitions

422

string_array = rule_engine.DataType.ARRAY(rule_engine.DataType.STRING)

423

number_array = rule_engine.DataType.ARRAY(rule_engine.DataType.FLOAT)

424

425

print(string_array.value_type.name) # "STRING"

426

print(number_array.value_type_nullable) # True (allows None values)

427

428

# Non-nullable arrays

429

strict_array = rule_engine.DataType.ARRAY(

430

rule_engine.DataType.STRING,

431

value_type_nullable=False

432

)

433

```

434

435

### Mapping Types

436

437

Define mapping types with specific key and value types.

438

439

```python

440

import rule_engine

441

442

# String keys, any values

443

string_mapping = rule_engine.DataType.MAPPING(rule_engine.DataType.STRING)

444

445

# Specific key and value types

446

typed_mapping = rule_engine.DataType.MAPPING(

447

rule_engine.DataType.STRING, # key type

448

rule_engine.DataType.FLOAT, # value type

449

value_type_nullable=False # values cannot be None

450

)

451

452

print(typed_mapping.key_type.name) # "STRING"

453

print(typed_mapping.value_type.name) # "FLOAT"

454

```

455

456

### Function Types

457

458

Define function types with return types and argument specifications.

459

460

```python

461

import rule_engine

462

463

# Basic function type

464

basic_func = rule_engine.DataType.FUNCTION(

465

"calculate",

466

return_type=rule_engine.DataType.FLOAT

467

)

468

469

# Function with argument types

470

typed_func = rule_engine.DataType.FUNCTION(

471

"process",

472

return_type=rule_engine.DataType.STRING,

473

argument_types=(rule_engine.DataType.STRING, rule_engine.DataType.FLOAT),

474

minimum_arguments=1 # Second argument is optional

475

)

476

477

print(typed_func.return_type.name) # "STRING"

478

print(len(typed_func.argument_types)) # 2

479

```

480

481

## Type System Integration

482

483

Using the type system with contexts and rules for comprehensive type safety:

484

485

```python

486

import rule_engine

487

488

# Define a complete type schema

489

type_schema = {

490

'user_id': rule_engine.DataType.FLOAT,

491

'name': rule_engine.DataType.STRING,

492

'active': rule_engine.DataType.BOOLEAN,

493

'tags': rule_engine.DataType.ARRAY(rule_engine.DataType.STRING),

494

'metadata': rule_engine.DataType.MAPPING(

495

rule_engine.DataType.STRING,

496

rule_engine.DataType.STRING

497

),

498

'created_at': rule_engine.DataType.DATETIME

499

}

500

501

# Create type resolver and context

502

type_resolver = rule_engine.type_resolver_from_dict(type_schema)

503

context = rule_engine.Context(type_resolver=type_resolver)

504

505

# Type-safe rule creation

506

rule = rule_engine.Rule(

507

'active and len(tags) > 0 and name =~ "Admin.*"',

508

context=context

509

)

510

511

# Validation happens at rule creation time

512

try:

513

invalid_rule = rule_engine.Rule('name + user_id', context=context) # Type error

514

except rule_engine.EvaluationError as e:

515

print(f"Type validation failed: {e.message}")

516

```