scalabeginner

Type Aliases and Abstract Types

Define type aliases, abstract types, and type members for cleaner and more expressive APIs.

scala
// Simple type aliases
type UserId = Long
type Username = String
type Email = String
type Timestamp = Long

// Function type aliases
type Predicate[A] = A => Boolean
type Transform[A] = A => A
type Handler[A, B] = A => Either[String, B]
type AsyncHandler[A, B] = A => scala.concurrent.Future[B]

// Complex generic aliases
type Result[A] = Either[String, A]
type Validated[A] = Either[List[String], A]
type Parser[A] = String => Result[A]

// Using aliases for cleaner signatures
def findUser(id: UserId): Result[Username] =
  if id > 0 then Right(s"User-$id")
  else Left("Invalid user ID")

def validateAge: Handler[String, Int] = input =>
  input.toIntOption match
    case Some(age) if age >= 0 && age <= 150 => Right(age)
    case Some(age) => Left(s"Age out of range: $age")
    case None => Left(s"Not a number: $input")

def isAdult: Predicate[Int] = _ >= 18
def double: Transform[Int] = _ * 2

// Abstract type members
trait Container:
  type Elem
  type Collection[_]
  def get(index: Int): Option[Elem]
  def size: Int

class IntListContainer(items: List[Int]) extends Container:
  type Elem = Int
  type Collection[A] = List[A]
  def get(index: Int): Option[Int] = items.lift(index)
  def size: Int = items.length

class StringArrayContainer(items: Array[String]) extends Container:
  type Elem = String
  type Collection[A] = Array[A]
  def get(index: Int): Option[String] = items.lift(index)
  def size: Int = items.length

// Path-dependent types
trait Database:
  type Row
  type Query
  def execute(q: Query): List[Row]

class PostgresDB extends Database:
  case class PgRow(data: Map[String, Any])
  type Row = PgRow
  type Query = String
  def execute(q: String): List[PgRow] =
    List(PgRow(Map("result" -> q)))

@main def run(): Unit =
  // Type aliases in action
  println(findUser(1))
  println(findUser(-1))
  println(validateAge("25"))
  println(validateAge("abc"))
  println(s"Is 20 adult: ${isAdult(20)}")
  println(s"Double 5: ${double(5)}")

  // Containers
  val intContainer = IntListContainer(List(10, 20, 30))
  val strContainer = StringArrayContainer(Array("a", "b", "c"))
  println(s"Int[1]: ${intContainer.get(1)}")
  println(s"Str[2]: ${strContainer.get(2)}")

  // Database
  val db = PostgresDB()
  val rows = db.execute("SELECT * FROM users")
  println(s"Rows: $rows")

  // Chaining with aliases
  val pipeline: List[Transform[Int]] = List(_ + 1, _ * 2, _ - 3)
  val result = pipeline.foldLeft(10)((acc, f) => f(acc))
  println(s"Pipeline result: $result")

Use Cases

  • Simplifying complex type signatures
  • Domain-specific type vocabulary
  • Abstract type members for flexibility

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.