Skip to content

Instantly share code, notes, and snippets.

@lbialy
Created November 2, 2024 10:45
Show Gist options
  • Save lbialy/46819689e0e6e0ff21c0545ae4849820 to your computer and use it in GitHub Desktop.
Save lbialy/46819689e0e6e0ff21c0545ae4849820 to your computer and use it in GitHub Desktop.
Making illegal states unrepresentable the Scala way
//> using scala 3.5.2
//> using dep "com.softwaremill.ox::core:0.5.2"
object types:
opaque type Title = String
object Title:
def apply(title: String): Either[Exception, Title] =
if title.isBlank then Left(Exception("Title must not be blank"))
else Right(title)
opaque type NonEmptyList[A] = (A, List[A])
extension [A](nel: NonEmptyList[A])
def toList: List[A] = nel match
case (a, list) => a :: list
object NonEmptyList:
def apply[A](a: A, as: A*): NonEmptyList[A] =
(a, as.toList)
opaque type ISBN = String
object ISBN:
private val isbnRegex = """^(?=(?:\D*\d){10}(?:(?:\D*\d){3})?$)[\d-]+$""".r
def apply(isbn: String): Either[Exception, ISBN] =
if isbnRegex.matches(isbn) then Right(isbn)
else Left(Exception("Not a valid ISBN"))
opaque type Author = String
object Author:
def apply(firstName: String, lastName: String): Either[Exception, Author] =
if firstName.nonEmpty && lastName.nonEmpty then Right(s"$firstName $lastName")
else Left(Exception("Author needs to have a first and last name"))
import types.*
case class Book(title: Title, isbn: ISBN, authors: NonEmptyList[Author])
@main def main = println {
for
validTitle <- Title("Functional programming in Scala")
validIsbn <- ISBN("9781617299582")
mpilquist <- Author("Michael", "Pilquist")
rbjarnason <- Author("Rúnar", "Bjarnason")
pchiusano <- Author("Paul", "Chiusano")
authorsNel = NonEmptyList(mpilquist, rbjarnason, pchiusano)
yield Book(validTitle, validIsbn, authorsNel)
}
import ox.either, either.ok
@main def mainOx = println {
either:
val title = Title("Functional programming in Scala").ok()
val isbn = ISBN("9781617299582").ok()
val authorsNel = NonEmptyList(
Author("Michael", "Pilquist").ok(),
Author("Rúnar", "Bjarnason").ok(),
Author("Paul", "Chiusano").ok()
)
Book(title, isbn, authorsNel)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment