Skip to main content
Version: 2.x

CaseClass

CaseClass represents an immutable case class in the IR. Use it when generating Scala code from data models, APIs, or structured dataโ€”it's the most frequently chosen type definition.

Use Casesโ€‹

  • Modeling product types (records) from OpenAPI schemas, JSON Schema, or data formats
  • Defining data transfer objects (DTOs) with named fields
  • Creating immutable value types with automatic equals, hashCode, copy

Constructionโ€‹

Build a case class with a name and field list:

import zio.blocks.codegen.ir._

val person = CaseClass(
name = "Person",
fields = List(
Field("id", TypeRef.Long),
Field("name", TypeRef.String),
Field("email", TypeRef.String)
)
)

With optional type parameters:

import zio.blocks.codegen.ir._

val box = CaseClass(
name = "Box",
fields = List(Field("value", TypeRef("T"))),
typeParams = List(TypeParam("T"))
)

With derives clauses:

import zio.blocks.codegen.ir._

val user = CaseClass(
name = "User",
fields = List(
Field("id", TypeRef.Long),
Field("name", TypeRef.String)
),
derives = List("Show", "Eq", "Codec")
)

Key Operationsโ€‹

All CaseClass instances support these core operations:

Accessing Componentsโ€‹

Extract parts of a case class:

import zio.blocks.codegen.ir._

val person = CaseClass(
name = "Person",
fields = List(
Field("id", TypeRef.Long),
Field("name", TypeRef.String),
Field("email", TypeRef.String)
)
)

Access each component by reading their properties:

import zio.blocks.codegen.ir._

val person = CaseClass(
name = "Person",
fields = List(
Field("id", TypeRef.Long),
Field("name", TypeRef.String),
Field("email", TypeRef.String)
)
)

Each component can be accessed and displayed:

import zio.blocks.codegen.ir._

person.name // "Person"
// res4: String = "Person"
person.fields // List[Field]
// res5: List[Field] = List(
// Field(
// name = "id",
// typeRef = TypeRef(name = "Long", typeArgs = List()),
// defaultValue = None,
// annotations = List(),
// doc = None
// ),
// Field(
// name = "name",
// typeRef = TypeRef(name = "String", typeArgs = List()),
// defaultValue = None,
// annotations = List(),
// doc = None
// ),
// Field(
// name = "email",
// typeRef = TypeRef(name = "String", typeArgs = List()),
// defaultValue = None,
// annotations = List(),
// doc = None
// )
// )
person.typeParams // List[TypeParam] (empty if not generic)
// res6: List[TypeParam] = List()
person.derives // List[String]
// res7: List[String] = List()
person.annotations // List[Annotation]
// res8: List[Annotation] = List()
person.isValueClass // Boolean
// res9: Boolean = false

Building with Copyโ€‹

Use the copy method to modify a case class:

import zio.blocks.codegen.ir._

val person = CaseClass(
name = "Person",
fields = List(
Field("id", TypeRef.Long),
Field("name", TypeRef.String),
Field("email", TypeRef.String)
)
)

Create a new version with additional fields:

import zio.blocks.codegen.ir._

val extended = person.copy(
fields = person.fields :+ Field("phone", TypeRef.String),
derives = List("Show")
)
// extended: CaseClass = CaseClass(
// name = "Person",
// fields = List(
// Field(
// name = "id",
// typeRef = TypeRef(name = "Long", typeArgs = List()),
// defaultValue = None,
// annotations = List(),
// doc = None
// ),
// Field(
// name = "name",
// typeRef = TypeRef(name = "String", typeArgs = List()),
// defaultValue = None,
// annotations = List(),
// doc = None
// ),
// Field(
// name = "email",
// typeRef = TypeRef(name = "String", typeArgs = List()),
// defaultValue = None,
// annotations = List(),
// doc = None
// ),
// Field(
// name = "phone",
// typeRef = TypeRef(name = "String", typeArgs = List()),
// defaultValue = None,
// annotations = List(),
// doc = None
// )
// ),
// typeParams = List(),
// extendsTypes = List(),
// derives = List("Show"),
// annotations = List(),
// companion = None,
// doc = None,
// isValueClass = false
// )

Adding a Companion Objectโ€‹

Case classes can have companion objects with static members:

import zio.blocks.codegen.ir._

val withCompanion = CaseClass(
name = "Config",
fields = List(Field("timeout", TypeRef.Long)),
companion = Some(
CompanionObject(
members = List(
ObjectMember.ValMember("Default", TypeRef("Config"), "Config(5000L)")
)
)
)
)

Examplesโ€‹

Here are concrete examples demonstrating typical CaseClass usage patterns:

Example 1: Simple Case Classโ€‹

A basic case class with primitive fields:

import zio.blocks.codegen.ir._
import zio.blocks.codegen.emit._

val order = CaseClass(
name = "Order",
fields = List(
Field("id", TypeRef.Long),
Field("total", TypeRef("BigDecimal")),
Field("status", TypeRef.String)
)
)

val file = ScalaFile(
packageDecl = PackageDecl("com.shop"),
types = List(order)
)

Emits:

import zio.blocks.codegen.emit._

ScalaEmitter.emit(file, EmitterConfig())
// res13: String = """package com.shop
//
// case class Order(
// id: Long,
// total: BigDecimal,
// status: String,
// )
// """

Example 2: Nested Typesโ€‹

A case class with optional and collection fields:

import zio.blocks.codegen.ir._
import zio.blocks.codegen.emit._

val user = CaseClass(
name = "User",
fields = List(
Field("id", TypeRef.Long),
Field("name", TypeRef.String),
Field("email", TypeRef("Option", List(TypeRef.String))),
Field("tags", TypeRef("List", List(TypeRef.String)))
)
)

val file = ScalaFile(
packageDecl = PackageDecl("com.example"),
types = List(user)
)

Emits:

import zio.blocks.codegen.emit._

ScalaEmitter.emit(file, EmitterConfig())
// res15: String = """package com.example
//
// case class User(
// id: Long,
// name: String,
// email: Option[String],
// tags: List[String],
// )
// """

Example 3: Generic Case Classโ€‹

A polymorphic case class with type parameters:

import zio.blocks.codegen.ir._
import zio.blocks.codegen.emit._

val page = CaseClass(
name = "Page",
fields = List(
Field("items", TypeRef("List", List(TypeRef("T")))),
Field("total", TypeRef.Long),
Field("pageSize", TypeRef.Int)
),
typeParams = List(TypeParam("T"))
)

val file = ScalaFile(
packageDecl = PackageDecl("com.example"),
types = List(page)
)

Emits:

import zio.blocks.codegen.emit._

ScalaEmitter.emit(file, EmitterConfig())
// res17: String = """package com.example
//
// case class Page[T](
// items: List[T],
// total: Long,
// pageSize: Int,
// )
// """

Example 4: Case Class with Derivesโ€‹

Automatic typeclass derivation:

import zio.blocks.codegen.ir._
import zio.blocks.codegen.emit._

val product = CaseClass(
name = "Product",
fields = List(
Field("id", TypeRef.String),
Field("name", TypeRef.String),
Field("price", TypeRef("java.math.BigDecimal"))
)
)

val file = ScalaFile(
packageDecl = PackageDecl("com.example"),
types = List(product)
)

Emits:

import zio.blocks.codegen.emit._

ScalaEmitter.emit(file, EmitterConfig())
// res19: String = """package com.example
//
// case class Product(
// id: String,
// name: String,
// price: java.math.BigDecimal,
// )
// """

Example 5: Case Class with Companionโ€‹

A case class with factory methods in the companion:

import zio.blocks.codegen.ir._
import zio.blocks.codegen.emit._

val config = CaseClass(
name = "AppConfig",
fields = List(
Field("host", TypeRef.String),
Field("port", TypeRef.Int)
),
companion = Some(
CompanionObject(
members = List(
ObjectMember.ValMember(
"Default",
TypeRef("AppConfig"),
"AppConfig(\"localhost\", 8080)"
)
)
)
)
)

val file = ScalaFile(
packageDecl = PackageDecl("com.example"),
types = List(config)
)

Emits:

import zio.blocks.codegen.emit._

ScalaEmitter.emit(file, EmitterConfig())
// res21: String = """package com.example
//
// case class AppConfig(
// host: String,
// port: Int,
// )
//
// object AppConfig {
// val Default: AppConfig = AppConfig("localhost", 8080)
// }
// """