Skip to content

Instantly share code, notes, and snippets.

@polentino
Created October 11, 2025 14:33
Show Gist options
  • Save polentino/f43e6ec552f1dec1c8b03c133d6caf30 to your computer and use it in GitHub Desktop.
Save polentino/f43e6ec552f1dec1c8b03c133d6caf30 to your computer and use it in GitHub Desktop.
final case class Person(name: String, age: Int, email: String)
final case class PersonBuilder[Name[_], Age[_], Email[_]] private (
private val name: Name[String],
private val age: Age[Int],
private val email: Email[String]
)
object PersonBuilder {
type Initial[A] = Option[A]
type Id[A] = A
def apply(): PersonBuilder[Initial, Initial, Initial] = PersonBuilder(None, None, None)
implicit final class NameOps[Age[_], Email[_]](private val builder: PersonBuilder[Initial, Age, Email]) {
def name(value: String) = builder.copy[Id, Age, Email](name = value)
}
implicit final class AgeSyntax[Name[_], Email[_]](private val builder: PersonBuilder[Name, Initial, Email]) {
def age(value: Int) = builder.copy[Name, Id, Email](age = value)
}
implicit final class EmailSyntax[Name[_], Age[_]](private val builder: PersonBuilder[Name, Age, Initial]) {
def email(value: String) = builder.copy[Name, Age, Id](email = value)
}
implicit final class BuildSyntax(private val builder: PersonBuilder[Id, Id, Id]) {
def build = Person(builder.name, builder.age, builder.email)
}
}
// PersonBuilder().build() // does not compile (missing 3 params)
// PersonBuilder().name("diego").build() // does not compile (missing 2 params)
// PersonBuilder().name("diego").name("diego") // does not compile (called .name(..) more than once)
// PersonBuilder().name("diego").age(39).build // does not compile (missing 1 param)
PersonBuilder().email("foo@bar").name("diego").age(39).build // val res0: Person = Person(diego,39,foo@bar)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment