or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

bridge.mdexpressions.mdfunctions.mdindex.mdoperations.mdserialization.mdtypeinfo.mdtypes.md
tile.json

typeinfo.mddocs/

Type Information Classes

Specialized TypeInformation implementations for Scala-specific types providing efficient serialization and type handling for case classes, Option, Either, Try, and collections.

CaseClassTypeInfo

Abstract class providing TypeInformation for Scala case classes with comprehensive field access and type introspection.

abstract class CaseClassTypeInfo[T](
  clazz: Class[T],
  typeParamTypeInfos: Array[TypeInformation[_]],
  fieldTypes: Seq[TypeInformation[_]],
  fieldNames: Seq[String]
) extends CompositeType[T] {
  
  // Field information
  def getFieldNames: Array[String]
  def getFieldIndex(fieldName: String): Int
  def getFieldIndices(fields: Array[String]): Array[Int]
  def getFieldTypes: Array[TypeInformation[_]]
  
  // Type introspection
  def getTypeAt[X](fieldExpression: String): TypeInformation[X]
  def getFlatFields(fieldExpression: String): List[FlatFieldDescriptor]
  def getGenericParameters: java.util.Map[String, TypeInformation[_]]
  
  // Comparator creation
  def createTypeComparatorBuilder(): TypeComparatorBuilder[T]
  
  // Standard TypeInformation methods
  def getTypeClass: Class[T]
  def isBasicType: Boolean = false
  def isTupleType: Boolean = true
  def getArity: Int = fieldTypes.length
  def getTotalFields: Int
  def isKeyType: Boolean
  def createSerializer(config: SerializerConfig): TypeSerializer[T]
}

Usage examples:

case class Product(id: Int, name: String, price: Double, category: String)

val productTypeInfo = Types.CASE_CLASS[Product]

// Field access
val fieldNames = productTypeInfo.getFieldNames  // Array("id", "name", "price", "category")
val nameIndex = productTypeInfo.getFieldIndex("name")  // 1
val priceType = productTypeInfo.getTypeAt[Double]("price")  // TypeInformation[Double]

// Multiple field indices
val indices = productTypeInfo.getFieldIndices(Array("id", "price"))  // Array(0, 2)

// Nested field access (for nested case classes)
case class Order(id: Int, product: Product, quantity: Int)
val orderTypeInfo = Types.CASE_CLASS[Order]
val productNameType = orderTypeInfo.getTypeAt[String]("product.name")

Flat Field Descriptors

For accessing nested fields in complex case class hierarchies:

case class FlatFieldDescriptor(position: Int, typeInfo: TypeInformation[_])

// Usage
val flatFields = orderTypeInfo.getFlatFields("product.*")
// Returns descriptors for all fields in the nested product

OptionTypeInfo

TypeInformation for Scala Option types with null-safe serialization.

class OptionTypeInfo[A, T <: Option[A]](elemTypeInfo: TypeInformation[A]) 
  extends TypeInformation[T] {
  
  def getElementTypeInfo: TypeInformation[A]
  def getTypeClass: Class[T]
  def isBasicType: Boolean = false
  def isTupleType: Boolean = false  
  def getArity: Int = 1
  def getTotalFields: Int = 1
  def isKeyType: Boolean  // Based on element type
  def createSerializer(config: SerializerConfig): TypeSerializer[T]
}

Usage examples:

case class User(id: Int, name: String, email: Option[String])

val userTypeInfo = Types.CASE_CLASS[User]
val emailFieldType = userTypeInfo.getTypeAt[Option[String]]("email")

// Option type info properties
val optStringTypeInfo = Types.OPTION(Types.STRING)
val elementType = optStringTypeInfo.getElementTypeInfo  // TypeInformation[String]
val isKey = optStringTypeInfo.isKeyType  // Based on String being a key type

// Direct Option handling
val optionalValue: Option[String] = Some("test")
val optionalTypeInfo = Types.of[Option[String]]

EitherTypeInfo

TypeInformation for Scala Either types supporting both Left and Right type information.

class EitherTypeInfo[A, B, T <: Either[A, B]](
  clazz: Class[T],
  leftTypeInfo: TypeInformation[A], 
  rightTypeInfo: TypeInformation[B]
) extends TypeInformation[T] {
  
  def getLeftTypeInfo: TypeInformation[A]
  def getRightTypeInfo: TypeInformation[B]
  def getTypeClass: Class[T]
  def isBasicType: Boolean = false
  def isTupleType: Boolean = false
  def getArity: Int = 2
  def getTotalFields: Int = 2  
  def isKeyType: Boolean = false  // Either is not a key type
  def createSerializer(config: SerializerConfig): TypeSerializer[T]
}

Usage examples:

// Either type for error handling
type ProcessingResult = Either[String, Int]  // Error message or success code

val eitherTypeInfo = Types.EITHER(Types.STRING, Types.INT)
val leftType = eitherTypeInfo.getLeftTypeInfo   // TypeInformation[String]
val rightType = eitherTypeInfo.getRightTypeInfo // TypeInformation[Int]

// Case class with Either field
case class TaskResult(id: Int, result: Either[String, Double])
val taskTypeInfo = Types.CASE_CLASS[TaskResult]
val resultFieldType = taskTypeInfo.getTypeAt[Either[String, Double]]("result")

// Usage in expressions
val processedResult = ifThenElse(
  $"success" === lit(true),
  lit(Right(42.0)),
  lit(Left("Processing failed"))
)

TryTypeInfo

TypeInformation for Scala Try types handling success and failure cases.

class TryTypeInfo[A, T <: Try[A]](valueType: TypeInformation[A]) 
  extends TypeInformation[T] {
  
  def getValueTypeInfo: TypeInformation[A]
  def getTypeClass: Class[T]
  def isBasicType: Boolean = false
  def isTupleType: Boolean = false
  def getArity: Int = 1
  def getTotalFields: Int = 1
  def isKeyType: Boolean = false  // Try is not a key type
  def createSerializer(config: SerializerConfig): TypeSerializer[T]
}

Usage examples:

import scala.util.{Try, Success, Failure}

// Try type for operations that might fail
val tryIntTypeInfo = Types.TRY(Types.INT)
val valueType = tryIntTypeInfo.getValueTypeInfo  // TypeInformation[Int]

// Case class with Try field
case class SafeOperation(id: Int, result: Try[Double])
val opTypeInfo = Types.CASE_CLASS[SafeOperation]

// Working with Try in table operations
val safeResult = table.select(
  $"id",
  ifThenElse(
    $"error".isNull,
    // Success case - create Success[Double]
    call("SUCCESS", $"value"),
    // Failure case - create Failure[Double]  
    call("FAILURE", $"error")
  ) as "result"
)

TraversableTypeInfo

Abstract base class for Scala collection types implementing TraversableOnce.

abstract class TraversableTypeInfo[T <: TraversableOnce[E], E](
  clazz: Class[T],
  elementTypeInfo: TypeInformation[E]
) extends TypeInformation[T] {
  
  def getElementTypeInfo: TypeInformation[E]
  def getTypeClass: Class[T]
  def isBasicType: Boolean = false
  def isTupleType: Boolean = false
  def getArity: Int = 1
  def getTotalFields: Int = 1
  def isKeyType: Boolean = false  // Collections are not key types
  
  // Abstract method - must be implemented by concrete collection types
  def createSerializer(config: SerializerConfig): TypeSerializer[T]
}

Usage examples:

// List type
val listTypeInfo = Types.TRAVERSABLE[List[String]]
val elementType = listTypeInfo.getElementTypeInfo  // TypeInformation[String]

// Set type
val setTypeInfo = Types.TRAVERSABLE[Set[Int]]

// Case class with collection fields
case class ShoppingCart(userId: Int, items: List[String], quantities: Array[Int])
val cartTypeInfo = Types.CASE_CLASS[ShoppingCart]
val itemsType = cartTypeInfo.getTypeAt[List[String]]("items")
val quantitiesType = cartTypeInfo.getTypeAt[Array[Int]]("quantities")

// Working with collections in tables
val processedCart = table.select(
  $"userId",
  size($"items") as "itemCount",
  array_contains($"items", lit("laptop")) as "hasLaptop"
)

EnumValueTypeInfo

TypeInformation for Scala Enumeration values with comparison and hashing support.

class EnumValueTypeInfo[E <: Enumeration](enum: E, clazz: Class[E#Value]) 
  extends TypeInformation[E#Value] with AtomicType[E#Value] {
  
  def getEnumeration: E
  def getTypeClass: Class[E#Value]
  def isBasicType: Boolean = true
  def isTupleType: Boolean = false
  def getArity: Int = 1
  def getTotalFields: Int = 1
  def isKeyType: Boolean = true  // Enumerations support comparison
  def createSerializer(config: SerializerConfig): TypeSerializer[E#Value]
  def createComparator(
    sortOrderAscending: Boolean, 
    executionConfig: ExecutionConfig
  ): TypeComparator[E#Value]
}

Usage examples:

// Define enumeration
object Priority extends Enumeration {
  type Priority = Value
  val LOW, MEDIUM, HIGH, CRITICAL = Value
}

// Create type information
val priorityTypeInfo = Types.ENUMERATION(Priority, classOf[Priority.Priority])
val enumeration = priorityTypeInfo.getEnumeration  // Priority object

// Usage in case class
case class Task(id: Int, name: String, priority: Priority.Priority)
val taskTypeInfo = Types.CASE_CLASS[Task]

// Enumeration in table operations
val highPriorityTasks = table.filter($"priority" === lit(Priority.HIGH))
val sortedTasks = table.orderBy($"priority".desc)  // Enumeration supports ordering

Specialized TypeInfo Classes

UnitTypeInfo

TypeInformation for Scala Unit type.

class UnitTypeInfo extends TypeInformation[Unit] with AtomicType[Unit] {
  def getTypeClass: Class[Unit] = classOf[Unit]
  def isBasicType: Boolean = true
  def isTupleType: Boolean = false
  def getArity: Int = 0
  def getTotalFields: Int = 0
  def isKeyType: Boolean = true
  def createSerializer(config: SerializerConfig): TypeSerializer[Unit]
  def createComparator(
    sortOrderAscending: Boolean,
    executionConfig: ExecutionConfig
  ): TypeComparator[Unit]
}

ScalaNothingTypeInfo

TypeInformation for Scala Nothing type (bottom type).

class ScalaNothingTypeInfo extends TypeInformation[Nothing] {
  def getTypeClass: Class[Nothing] = classOf[Nothing]
  def isBasicType: Boolean = true
  def isTupleType: Boolean = false
  def getArity: Int = 0
  def getTotalFields: Int = 0
  def isKeyType: Boolean = false  // Nothing cannot be instantiated
  def createSerializer(config: SerializerConfig): TypeSerializer[Nothing]
}

Type Information Composition

Nested Case Classes

case class Address(street: String, city: String, zip: String)
case class Person(name: String, age: Int, address: Address)

val personTypeInfo = Types.CASE_CLASS[Person]

// Access nested fields
val streetType = personTypeInfo.getTypeAt[String]("address.street")
val cityType = personTypeInfo.getTypeAt[String]("address.city")

// Flat field access for all address fields
val addressFields = personTypeInfo.getFlatFields("address.*")

Generic Case Classes

case class Container[T](id: Int, content: T)

// Explicit type parameter specification
val stringContainerType = Types.CASE_CLASS[Container[String]]
val intContainerType = Types.CASE_CLASS[Container[Int]]

// Generic parameter information
val genericParams = stringContainerType.getGenericParameters
// Map("T" -> TypeInformation[String])

Collection of Case Classes

case class Order(id: Int, amount: Double)

val orderListType = Types.TRAVERSABLE[List[Order]]
val orderElementType = orderListType.getElementTypeInfo  // CaseClassTypeInfo[Order]

// Nested access in collections
val ordersTable = table.select(
  $"customerId",
  $"orders",
  size($"orders") as "orderCount",
  $"orders"[0].get("amount") as "firstOrderAmount"
)

Performance Characteristics

Serialization Efficiency

  • Case Classes: Optimized field-by-field serialization
  • Option: Null-bit optimization for Some/None representation
  • Either: Tagged union serialization with type discrimination
  • Try: Exception information preserved in Failure cases
  • Collections: Element-wise serialization with size prefixing
  • Enumerations: Ordinal-based serialization for space efficiency

Memory Layout

  • Case classes use packed field layouts
  • Optional fields use null-bit vectors for space efficiency
  • Collections use optimized array layouts when possible
  • Nested structures maintain reference locality

Comparison Support

  • Enumerations support natural ordering by ordinal
  • Case classes support field-by-field comparison
  • Option types delegate comparison to element type
  • Collections support lexicographic ordering

Best Practices

Type Information Reuse

// Cache frequently used type information
val userTypeInfo = Types.CASE_CLASS[User]
val userListTypeInfo = Types.TRAVERSABLE[List[User]]

// Reuse in multiple contexts
val usersTable = tEnv.fromDataStream(userStream)(userTypeInfo)
val processedUsers = table.select($"*")(userTypeInfo)

Nested Type Access

// Use type-safe field access
val nestedFieldType = complexTypeInfo.getTypeAt[String]("path.to.field")

// Prefer flat field access for performance
val flatFields = typeInfo.getFlatFields("*")

Generic Type Handling

// Explicit type specification for generics
val optionalUserType = Types.OPTION(Types.CASE_CLASS[User])
val eitherResultType = Types.EITHER(Types.STRING, Types.CASE_CLASS[Result])

// Type-safe generic operations
def createOptionalType[T: TypeInformation]: TypeInformation[Option[T]] = {
  Types.OPTION(Types.of[T])
}