scalaadvanced
Opaque Types for Type Safety
Use Scala 3 opaque types to create zero-cost type wrappers for domain modeling.
scalaPress ⌘/Ctrl + Shift + C to copy
// Opaque types: zero-cost type wrappers
object Types:
opaque type UserId = Long
opaque type OrderId = Long
opaque type Email = String
opaque type NonEmptyString = String
opaque type Percentage = Double
object UserId:
def apply(id: Long): UserId = id
extension (id: UserId)
def value: Long = id
def next: UserId = id + 1
object OrderId:
def apply(id: Long): OrderId = id
extension (id: OrderId)
def value: Long = id
object Email:
def apply(value: String): Either[String, Email] =
if value.matches(".+@.+\\..+") then Right(value)
else Left(s"Invalid email: $value")
def unsafe(value: String): Email = value
extension (email: Email)
def value: String = email
def domain: String = email.split("@").last
def local: String = email.split("@").head
object NonEmptyString:
def apply(s: String): Option[NonEmptyString] =
if s.nonEmpty then Some(s) else None
extension (s: NonEmptyString)
def value: String = s
def length: Int = s.length
object Percentage:
def apply(value: Double): Either[String, Percentage] =
if value >= 0 && value <= 100 then Right(value)
else Left(s"$value is not a valid percentage")
extension (p: Percentage)
def value: Double = p
def asFraction: Double = p / 100.0
def format: String = f"$p%.1f%%"
import Types.*
// Can't accidentally mix UserId and OrderId
def getOrder(userId: UserId, orderId: OrderId): String =
s"User ${userId.value} -> Order ${orderId.value}"
def sendEmail(to: Email, subject: NonEmptyString): Unit =
println(s"Sending '$subject' to ${to.value}")
@main def run(): Unit =
val userId = UserId(42)
val orderId = OrderId(100)
// This would NOT compile:
// getOrder(orderId, userId) // type mismatch!
println(getOrder(userId, orderId))
// Validated construction
Email("alice@test.com") match
case Right(email) =>
println(s"Domain: ${email.domain}")
println(s"Local: ${email.local}")
case Left(err) =>
println(err)
Email("not-an-email") match
case Left(err) => println(s"Rejected: $err")
case _ => ()
for
pct <- Percentage(75.5)
do
println(s"${pct.format} = ${pct.asFraction}")
// NonEmptyString
NonEmptyString("Hello").foreach { s =>
println(s"Non-empty: $s (len=${s.length})")
}
println(s"Empty: ${NonEmptyString("")}")Use Cases
- Domain-driven type safety
- Preventing primitive obsession
- Zero-cost validated wrappers
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
scalaintermediate
Multiversal Equality in Scala 3
Use strict equality with CanEqual to prevent comparing unrelated types at compile time.
Best for: Preventing accidental type comparisons
#scala#equality
scalaadvanced
Phantom Types for Compile-Time Safety
Use phantom types to encode state and constraints at the type level without runtime cost.
Best for: State machine enforcement at compile time
#scala#phantom-types
scalaintermediate
Type-Safe Identifiers Pattern
Create type-safe ID wrappers to prevent mixing different entity IDs at compile time.
Best for: Preventing entity ID mixups
#scala#type-safety
scalabeginner
Scala Hello World Application
Create a basic Scala application with main method, string interpolation, and val/var basics.
Best for: Getting started with Scala
#scala#basics