Created
November 2, 2024 10:45
-
-
Save lbialy/46819689e0e6e0ff21c0545ae4849820 to your computer and use it in GitHub Desktop.
Making illegal states unrepresentable the Scala way
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//> 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