4. Background
ZIO Prelude is a Scala-first take on functional abstractions
• Type classes to eliminate duplication / boilerplate
5. Background
ZIO Prelude is a Scala-first take on functional abstractions
• Type classes to eliminate duplication / boilerplate
• Data types that complement the Scala standard library
(NonEmptyList, ZPure)
6. Background
ZIO Prelude is a Scala-first take on functional abstractions
• Type classes to eliminate duplication / boilerplate
• Data types that complement the Scala standard library
(NonEmptyList, ZPure)
• Newtypes / subtypes
16. Smart Constructors
final case class Name private(value: String)
object Name:
def make(value: String): Either[String, Name] = ???
final case class Email private(value: String)
object Email:
def make(value: String): Either[String, Email] = ???
final case class Address private(value: String)
object Address:
def make(value: String): Either[String, Address] = ???
final case class Person(
name: Name,
email: Email,
address: Address
)
17. Smart Constructors
final case class Name private(value: String)
object Name:
def make(value: String): Either[String, Name] = ???
final case class Email private(value: String)
object Email:
def make(value: String): Either[String, Email] = ???
final case class Address private(value: String)
object Address:
def make(value: String): Either[String, Address] = ???
final case class Person(
name: Name,
email: Email,
address: Address
)
18. Smart Constructors
final case class Name private(value: String)
object Name:
def make(value: String): Either[String, Name] = ???
final case class Email private(value: String)
object Email:
def make(value: String): Either[String, Email] = ???
final case class Address private(value: String)
object Address:
def make(value: String): Either[String, Address] = ???
final case class Person(
name: Name,
email: Email,
address: Address
)
19. Smart Constructors
final case class Name private(value: String)
object Name:
def make(value: String): Either[String, Name] = ???
final case class Email private(value: String)
object Email:
def make(value: String): Either[String, Email] = ???
final case class Address private(value: String)
object Address:
def make(value: String): Either[String, Address] = ???
final case class Person(
name: Name,
email: Email,
address: Address
)
20. Smart Constructors
final case class Name private(value: String)
object Name:
def make(value: String): Either[String, Name] = ???
final case class Email private(value: String)
object Email:
def make(value: String): Either[String, Email] = ???
final case class Address private(value: String)
object Address:
def make(value: String): Either[String, Address] = ???
final case class Person(
name: Name,
email: Email,
address: Address
)
21.
22.
23. Smart Constructors
val person1: Either[String, Person] =
for {
name <- Name.make("Jorge")
email <- Email.make("jorge.vasquez@scalac.io")
address <- Address.make("100 Some St.")
} yield Person(name, email, address)
val person2: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
24. Smart Constructors
val person1: Either[String, Person] =
for {
name <- Name.make("Jorge")
email <- Email.make("jorge.vasquez@scalac.io")
address <- Address.make("100 Some St.")
} yield Person(name, email, address)
val person2: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
25. Smart Constructors
val person1: Either[String, Person] =
for {
name <- Name.make("Jorge")
email <- Email.make("jorge.vasquez@scalac.io")
address <- Address.make("100 Some St.")
} yield Person(name, email, address)
val person2: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
26. Smart Constructors
val person1: Either[String, Person] =
for {
name <- Name.make("Jorge")
email <- Email.make("jorge.vasquez@scalac.io")
address <- Address.make("100 Some St.")
} yield Person(name, email, address)
val person2: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
27. Smart Constructors
val person1: Either[String, Person] =
for {
name <- Name.make("Jorge")
email <- Email.make("jorge.vasquez@scalac.io")
address <- Address.make("100 Some St.")
} yield Person(name, email, address)
val person2: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
28. Smart Constructors
val person1: Either[String, Person] =
for {
name <- Name.make("Jorge")
email <- Email.make("jorge.vasquez@scalac.io")
address <- Address.make("100 Some St.")
} yield Person(name, email, address)
val person2: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
29. Smart Constructors
val person1: Either[String, Person] =
for {
name <- Name.make("Jorge")
email <- Email.make("jorge.vasquez@scalac.io")
address <- Address.make("100 Some St.")
} yield Person(name, email, address)
val person2: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
30. Smart Constructors
val person1: Either[String, Person] =
for {
name <- Name.make("Jorge")
email <- Email.make("jorge.vasquez@scalac.io")
address <- Address.make("100 Some St.")
} yield Person(name, email, address)
val person2: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
31. Smart Constructors
val person1: Either[String, Person] =
for {
name <- Name.make("Jorge")
email <- Email.make("jorge.vasquez@scalac.io")
address <- Address.make("100 Some St.")
} yield Person(name, email, address)
val person2: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
32. Smart Constructors
val person1: Either[String, Person] =
for {
name <- Name.make("Jorge")
email <- Email.make("jorge.vasquez@scalac.io")
address <- Address.make("100 Some St.")
} yield Person(name, email, address)
val person2: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
33. Smart Constructors
val person1: Either[String, Person] =
for {
name <- Name.make("Jorge")
email <- Email.make("jorge.vasquez@scalac.io")
address <- Address.make("100 Some St.")
} yield Person(name, email, address)
val person2: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
34. Opaque types +
Smart Constructors
opaque type Name = String
object Name:
def make(value: String): Either[String, Name] = ???
opaque type Email = String
object Email:
def make(value: String): Either[String, Email] = ???
opaque type Address = String
object Address:
def make(value: String): Either[String, Address] = ???
final case class Person(
name: Name,
email: Email,
address: Address
)
35. Opaque types +
Smart Constructors
opaque type Name = String
object Name:
def make(value: String): Either[String, Name] = ???
opaque type Email = String
object Email:
def make(value: String): Either[String, Email] = ???
opaque type Address = String
object Address:
def make(value: String): Either[String, Address] = ???
final case class Person(
name: Name,
email: Email,
address: Address
)
36. Opaque types +
Smart Constructors
opaque type Name = String
object Name:
def make(value: String): Either[String, Name] = ???
opaque type Email = String
object Email:
def make(value: String): Either[String, Email] = ???
opaque type Address = String
object Address:
def make(value: String): Either[String, Address] = ???
final case class Person(
name: Name,
email: Email,
address: Address
)
37. Opaque types +
Smart Constructors
opaque type Name = String
object Name:
def make(value: String): Either[String, Name] = ???
opaque type Email = String
object Email:
def make(value: String): Either[String, Email] = ???
opaque type Address = String
object Address:
def make(value: String): Either[String, Address] = ???
final case class Person(
name: Name,
email: Email,
address: Address
)
38. Opaque types +
Smart Constructors
opaque type Name = String
object Name:
def make(value: String): Either[String, Name] = ???
opaque type Email = String
object Email:
def make(value: String): Either[String, Email] = ???
opaque type Address = String
object Address:
def make(value: String): Either[String, Address] = ???
final case class Person(
name: Name,
email: Email,
address: Address
)
39.
40. Opaque types +
Smart Constructors
val person1: Either[String, Person] =
for {
name <- Name.make("Jorge")
email <- Email.make("jorge.vasquez@scalac.io")
address <- Address.make("100 Some St.")
} yield Person(name, email, address)
val person2: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
41.
42.
43. Wouldn't it be dreamy if we
could have more precise data
modelling, without unnecessary
overhead?
51. Smarter constructors!
val name1: Name = Name("Jorge")
val email1: Email = Email("jorge.vasquez@scalac.io")
val address1: Address = Address("100 Some St.")
val person1: Person = Person(name1, email1, address1)
52. Smarter constructors!
val name1: Name = Name("Jorge")
val email1: Email = Email("jorge.vasquez@scalac.io")
val address1: Address = Address("100 Some St.")
val person1: Person = Person(name1, email1, address1)
53. Smarter constructors!
val name1: Name = Name("Jorge")
val email1: Email = Email("jorge.vasquez@scalac.io")
val address1: Address = Address("100 Some St.")
val person1: Person = Person(name1, email1, address1)
54. Smarter constructors!
val name1: Name = Name("Jorge")
val email1: Email = Email("jorge.vasquez@scalac.io")
val address1: Address = Address("100 Some St.")
val person1: Person = Person(name1, email1, address1)
55. Smarter constructors!
val name1: Name = Name("Jorge")
val email1: Email = Email("jorge.vasquez@scalac.io")
val address1: Address = Address("100 Some St.")
val person1: Person = Person(name1, email1, address1)
56. Smarter constructors!
val name2: Name = Name("") // COMPILATION ERROR! must not be empty
val email2: Email = Email("whatever") // COMPILATION ERROR! must match regex
val address2: Address = Address("") // COMPILATION ERROR! must not be empty
val person2: Person = Person(name2, email2, address2)
57. Smarter constructors!
val name2: Name = Name("") // COMPILATION ERROR! must not be empty
val email2: Email = Email("whatever") // COMPILATION ERROR! must match regex
val address2: Address = Address("") // COMPILATION ERROR! must not be empty
val person2: Person = Person(name2, email2, address2)
58. Smarter constructors!
val name2: Name = Name("") // COMPILATION ERROR! must not be empty
val email2: Email = Email("whatever") // COMPILATION ERROR! must match regex
val address2: Address = Address("") // COMPILATION ERROR! must not be empty
val person2: Person = Person(name2, email2, address2)
59. Smarter constructors!
val name2: Name = Name("") // COMPILATION ERROR! must not be empty
val email2: Email = Email("whatever") // COMPILATION ERROR! must match regex
val address2: Address = Address("") // COMPILATION ERROR! must not be empty
val person2: Person = Person(name2, email2, address2)
60. Smarter constructors!
val name2: Name = Name("") // COMPILATION ERROR! must not be empty
val email2: Email = Email("whatever") // COMPILATION ERROR! must match regex
val address2: Address = Address("") // COMPILATION ERROR! must not be empty
val person2: Person = Person(name2, email2, address2)
61. Smarter constructors!
val name3: Either[String, Name] = Name(scala.io.StdIn.readLine())
val email3: Either[String, Email] = Email(scala.io.StdIn.readLine())
val address3: Either[String, Address] = Address(scala.io.StdIn.readLine())
val person3: Either[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
62. Smarter constructors!
val name3: Either[String, Name] = Name(scala.io.StdIn.readLine())
val email3: Either[String, Email] = Email(scala.io.StdIn.readLine())
val address3: Either[String, Address] = Address(scala.io.StdIn.readLine())
val person3: Either[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
63. Smarter constructors!
val name3: Either[String, Name] = Name(scala.io.StdIn.readLine())
val email3: Either[String, Email] = Email(scala.io.StdIn.readLine())
val address3: Either[String, Address] = Address(scala.io.StdIn.readLine())
val person3: Either[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
64. Smarter constructors!
val name3: Either[String, Name] = Name(scala.io.StdIn.readLine())
val email3: Either[String, Email] = Email(scala.io.StdIn.readLine())
val address3: Either[String, Address] = Address(scala.io.StdIn.readLine())
val person3: Either[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
65. Smarter constructors!
val name3: Either[String, Name] = Name(scala.io.StdIn.readLine())
val email3: Either[String, Email] = Email(scala.io.StdIn.readLine())
val address3: Either[String, Address] = Address(scala.io.StdIn.readLine())
val person3: Either[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
66. No wrapping/unwrapping!
type Natural <: Int
inline def Natural: Int Refined Natural = Refined {
greaterThanEqual(0)
}
type Age <: Int
inline def Age: Int Refined Age = Refined {
greaterThanEqual(0) && lessThanEqual(150)
}
val natural: Natural = Natural(5)
val age: Age = Age(60)
def add(a: Int, b: Int): Int = a + b
add(natural, age)
// 65
67. No wrapping/unwrapping!
type Natural <: Int
inline def Natural: Int Refined Natural = Refined {
greaterThanEqual(0)
}
type Age <: Int
inline def Age: Int Refined Age = Refined {
greaterThanEqual(0) && lessThanEqual(150)
}
val natural: Natural = Natural(5)
val age: Age = Age(60)
def add(a: Int, b: Int): Int = a + b
add(natural, age)
// 65
68. No wrapping/unwrapping!
type Natural <: Int
inline def Natural: Int Refined Natural = Refined {
greaterThanEqual(0)
}
type Age <: Int
inline def Age: Int Refined Age = Refined {
greaterThanEqual(0) && lessThanEqual(150)
}
val natural: Natural = Natural(5)
val age: Age = Age(60)
def add(a: Int, b: Int): Int = a + b
add(natural, age)
// 65
69. No wrapping/unwrapping!
type Natural <: Int
inline def Natural: Int Refined Natural = Refined {
greaterThanEqual(0)
}
type Age <: Int
inline def Age: Int Refined Age = Refined {
greaterThanEqual(0) && lessThanEqual(150)
}
val natural: Natural = Natural(5)
val age: Age = Age(60)
def add(a: Int, b: Int): Int = a + b
add(natural, age)
// 65
70. No wrapping/unwrapping!
type Natural <: Int
inline def Natural: Int Refined Natural = Refined {
greaterThanEqual(0)
}
type Age <: Int
inline def Age: Int Refined Age = Refined {
greaterThanEqual(0) && lessThanEqual(150)
}
val natural: Natural = Natural(5)
val age: Age = Age(60)
def add(a: Int, b: Int): Int = a + b
add(natural, age)
// 65
71. No wrapping/unwrapping!
type Natural <: Int
inline def Natural: Int Refined Natural = Refined {
greaterThanEqual(0)
}
type Age <: Int
inline def Age: Int Refined Age = Refined {
greaterThanEqual(0) && lessThanEqual(150)
}
val natural: Natural = Natural(5)
val age: Age = Age(60)
def add(a: Int, b: Int): Int = a + b
add(natural, age)
// 65
72. No wrapping/unwrapping!
type Natural <: Int
inline def Natural: Int Refined Natural = Refined {
greaterThanEqual(0)
}
type Age <: Int
inline def Age: Int Refined Age = Refined {
greaterThanEqual(0) && lessThanEqual(150)
}
val natural: Natural = Natural(5)
val age: Age = Age(60)
def add(a: Int, b: Int): Int = a + b
add(natural, age)
// 65
73. No wrapping/unwrapping!
type Natural <: Int
inline def Natural: Int Refined Natural = Refined {
greaterThanEqual(0)
}
type Age <: Int
inline def Age: Int Refined Age = Refined {
greaterThanEqual(0) && lessThanEqual(150)
}
val natural: Natural = Natural(5)
val age: Age = Age(60)
def add(a: Int, b: Int): Int = a + b
add(natural, age)
// 65
77. Summary
• We need more precise data modelling for our
applications
• We can use case classes with smart constructors. This
imposes additional runtime overhead
78. Summary
• We need more precise data modelling for our
applications
• We can use case classes with smart constructors. This
imposes additional runtime overhead
• Using opaque types removes some of the overhead, but
not all of it
80. Summary
ZIO Prelude Refinement Types will provide a solution to these
problems:
• Smarter Constructors with Composable Assertions that work at
compile time AND at run time
81. Summary
ZIO Prelude Refinement Types will provide a solution to these
problems:
• Smarter Constructors with Composable Assertions that work at
compile time AND at run time
• No additional overhead because of wrapping/unwrapping
82. Summary
ZIO Prelude Refinement Types will provide a solution to these
problems:
• Smarter Constructors with Composable Assertions that work at
compile time AND at run time
• No additional overhead because of wrapping/unwrapping
• All of this thanks to the power of Scala 3 Macros!. No weird stuff,
just standard Scala