SlideShare a Scribd company logo
1 of 178
Download to read offline
Be Smart,
Constrain Your Types
to Free Your Brain!
Functional Scala
December 3rd, 2021
Jorge Vásquez
Scala Developer
@Scalac
ZIO Prelude contributor
Background
ZIO Prelude is a Scala-first take on functional abstractions
Background
ZIO Prelude is a Scala-first take on functional abstractions
• Type classes to describe the ways different types are
similar
Background
ZIO Prelude is a Scala-first take on functional abstractions
• Type classes to describe the ways different types are
similar
• Data types that complement the Scala standard library
(NonEmptyList, ZValidation, ZPure)
Background
ZIO Prelude is a Scala-first take on functional abstractions
• Type classes to describe the ways different types are
similar
• Data types that complement the Scala standard library
(NonEmptyList, ZValidation, ZPure)
• Smart Types
Problem
Precise Data
Modelling
Representing
everything with
Simple Types
final case class Person(
name: String,
email: String,
address: String
)
Representing
everything with
Simple Types
final case class Person(
name: String,
email: String,
address: String
)
Representing
everything with
Simple Types
final case class Person(
name: String,
email: String,
address: String
)
Representing
everything with
Simple Types
final case class Person(
name: String,
email: String,
address: String
)
Representing
everything with
Simple Types
final case class Person(
name: String,
email: String,
address: String
)
Storing invalid data
in databases
name email address
t1@tst.co whatever
Main Av. t2@tst.co
Applications corrupting third
party systems
Applications throwing exceptions
Smart Constructors
final case class Name private (name: String)
object Name {
def make(value: String): Either[String, Name] =
if (value.nonEmpty) Right(Name(value))
else Left("Name cannot be empty")
}
Smart Constructors
final case class Name private (name: String)
object Name {
def make(value: String): Either[String, Name] =
if (value.nonEmpty) Right(Name(value))
else Left("Name cannot be empty")
}
Smart Constructors
final case class Name private (name: String)
object Name {
def make(value: String): Either[String, Name] =
if (value.nonEmpty) Right(Name(value))
else Left("Name cannot be empty")
}
Smart Constructors
final case class Name private (name: String)
object Name {
def make(value: String): Either[String, Name] =
if (value.nonEmpty) Right(Name(value))
else Left("Name cannot be empty")
}
Smart Constructors
final case class Email private (email: String)
object Email {
def make(value: String): Either[String, Email] = {
val regex = "^[w-.]+@([w-]+.)+[w-]{2,4}$"
if (value.matches(regex)) Right(Email(value))
else Left(s"$value does not match $regex")
}
}
Smart Constructors
final case class Address private (address: String)
object Address {
def make(value: String): Either[String, Address] =
if (value.nonEmpty) Right(Address(value))
else Left("Address cannot be empty")
}
final case class Person(name: Name, email: Email, address: Address)
Smart Constructors
final case class Address private (address: String)
object Address {
def make(value: String): Either[String, Address] =
if (value.nonEmpty) Right(Address(value))
else Left("Address cannot be empty")
}
final case class Person(name: Name, email: Email, address: Address)
Smart Constructors
final case class Address private (address: String)
object Address {
def make(value: String): Either[String, Address] =
if (value.nonEmpty) Right(Address(value))
else Left("Address cannot be empty")
}
final case class Person(name: Name, email: Email, address: Address)
Smart Constructors
val person: 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)
Smart Constructors
val person: 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)
Smart Constructors
val person: 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)
Smart Constructors
val person: 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)
Smart Constructors
val person: 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)
Smart Constructors
val person: 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)
Smart Constructors
val person: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
Smart Constructors
val person: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
Smart Constructors
val person: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
Smart Constructors
val person: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
Smart Constructors
val person: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
Smart Constructors
val person: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
Using the Newtype library
import io.estatico.newtype.macros.newtype
import io.estatico.newtype.ops._
@newtype class Name(name: String)
object Name {
def make(value: String): Either[String, Name] =
if (value.nonEmpty) Right(value.coerce)
else Left("Name cannot be empty")
}
Using the Newtype library
import io.estatico.newtype.macros.newtype
import io.estatico.newtype.ops._
@newtype class Email(email: String)
object Email {
def make(value: String): Either[String, Email] = {
val regex = "^[w-.]+@([w-]+.)+[w-]{2,4}$"
if (value.matches(regex)) Right(value.coerce)
else Left(s"$value does not match $regex")
}
}
Using the Newtype library
import io.estatico.newtype.macros.newtype
import io.estatico.newtype.ops._
@newtype class Address(address: String)
object Address {
def make(value: String): Either[String, Address] =
if (value.nonEmpty) Right(value.coerce)
else Left("Address cannot be empty")
}
final case class Person(name: Name, email: Email, address: Address)
Using the Newtype library
import io.estatico.newtype.macros.newtype
import io.estatico.newtype.ops._
@newtype class Address(address: String)
object Address {
def make(value: String): Either[String, Address] =
if (value.nonEmpty) Right(value.coerce)
else Left("Address cannot be empty")
}
final case class Person(name: Name, email: Email, address: Address)
Using the Newtype library
import io.estatico.newtype.macros.newtype
import io.estatico.newtype.ops._
@newtype class Address(address: String)
object Address {
def make(value: String): Either[String, Address] =
if (value.nonEmpty) Right(value.coerce)
else Left("Address cannot be empty")
}
final case class Person(name: Name, email: Email, address: Address)
Newtype library
val person: 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)
Newtype library
val person: Either[String, Person] =
for {
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
} yield Person(name, email, address)
Using the Refined library
import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.collection._
import eu.timepit.refined.string._
type Name = String Refined NonEmpty
type Email = String Refined MatchesRegex["^[w-.]+@([w-]+.)+[w-]{2,4}$"] // This works since Scala 2.13 thanks to literal types
type Email = String Refined MatchesRegex[W.`^[w-.]+@([w-]+.)+[w-]{2,4}$`.T] // Before Scala 2.13
type Address = String Refined NonEmpty
final case class Person(name: Name, email: Email, address: Address)
Using the Refined library
import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.collection._
import eu.timepit.refined.string._
type Name = String Refined NonEmpty
type Email = String Refined MatchesRegex["^[w-.]+@([w-]+.)+[w-]{2,4}$"] // This works since Scala 2.13 thanks to literal types
type Email = String Refined MatchesRegex[W.`^[w-.]+@([w-]+.)+[w-]{2,4}$`.T] // Before Scala 2.13
type Address = String Refined NonEmpty
final case class Person(name: Name, email: Email, address: Address)
Using the Refined library
import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.collection._
import eu.timepit.refined.string._
type Name = String Refined NonEmpty
type Email = String Refined MatchesRegex["^[w-.]+@([w-]+.)+[w-]{2,4}$"] // This works since Scala 2.13 thanks to literal types
type Email = String Refined MatchesRegex[W.`^[w-.]+@([w-]+.)+[w-]{2,4}$`.T] // Before Scala 2.13
type Address = String Refined NonEmpty
final case class Person(name: Name, email: Email, address: Address)
Using the Refined library
import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.collection._
import eu.timepit.refined.string._
type Name = String Refined NonEmpty
type Email = String Refined MatchesRegex["^[w-.]+@([w-]+.)+[w-]{2,4}$"] // This works since Scala 2.13 thanks to literal types
type Email = String Refined MatchesRegex[W.`^[w-.]+@([w-]+.)+[w-]{2,4}$`.T] // Before Scala 2.13
type Address = String Refined NonEmpty
final case class Person(name: Name, email: Email, address: Address)
Using the Refined library
import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.collection._
import eu.timepit.refined.string._
type Name = String Refined NonEmpty
type Email = String Refined MatchesRegex["^[w-.]+@([w-]+.)+[w-]{2,4}$"] // This works since Scala 2.13 thanks to literal types
type Email = String Refined MatchesRegex[W.`^[w-.]+@([w-]+.)+[w-]{2,4}$`.T] // Before Scala 2.13
type Address = String Refined NonEmpty
final case class Person(name: Name, email: Email, address: Address)
Using the Refined library
import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.collection._
import eu.timepit.refined.string._
type Name = String Refined NonEmpty
type Email = String Refined MatchesRegex["^[w-.]+@([w-]+.)+[w-]{2,4}$"] // This works since Scala 2.13 thanks to literal types
type Email = String Refined MatchesRegex[W.`^[w-.]+@([w-]+.)+[w-]{2,4}$`.T] // Before Scala 2.13
type Address = String Refined NonEmpty
final case class Person(name: Name, email: Email, address: Address)
Compile-time
validations with
Refined
val name1: Name = "Jorge"
val email1: Email = "jorge.vasquez@scalac.io"
val address1: Address = "100 Some St."
val person1: Person = Person(name1, email1, address1)
Compile-time
validations with
Refined
val name1: Name = "Jorge"
val email1: Email = "jorge.vasquez@scalac.io"
val address1: Address = "100 Some St."
val person1: Person = Person(name1, email1, address1)
Compile-time
validations with
Refined
val name1: Name = "Jorge"
val email1: Email = "jorge.vasquez@scalac.io"
val address1: Address = "100 Some St."
val person1: Person = Person(name1, email1, address1)
Compile-time
validations with
Refined
val name1: Name = "Jorge"
val email1: Email = "jorge.vasquez@scalac.io"
val address1: Address = "100 Some St."
val person1: Person = Person(name1, email1, address1)
Compile-time
validations with
Refined
val name1: Name = "Jorge"
val email1: Email = "jorge.vasquez@scalac.io"
val address1: Address = "100 Some St."
val person1: Person = Person(name1, email1, address1)
Compile-time
validations with
Refined
val name2: Name = ""
// Predicate isEmpty() did not fail
val email2: Email = "whatever"
// Predicate failed:
// "whatever".matches("^[w-.]+@([w-]+.)+[w-]{2,4}$").
val address2: Address = ""
// Predicate isEmpty() did not fail
val person2: Person = Person(name2, email2, address2)
Compile-time
validations with
Refined
val name2: Name = ""
// Predicate isEmpty() did not fail
val email2: Email = "whatever"
// Predicate failed:
// "whatever".matches("^[w-.]+@([w-]+.)+[w-]{2,4}$").
val address2: Address = ""
// Predicate isEmpty() did not fail
val person2: Person = Person(name2, email2, address2)
Compile-time
validations with
Refined
val name2: Name = ""
// Predicate isEmpty() did not fail
val email2: Email = "whatever"
// Predicate failed:
// "whatever".matches("^[w-.]+@([w-]+.)+[w-]{2,4}$").
val address2: Address = ""
// Predicate isEmpty() did not fail
val person2: Person = Person(name2, email2, address2)
Compile-time
validations with
Refined
val name2: Name = ""
// Predicate isEmpty() did not fail
val email2: Email = "whatever"
// Predicate failed:
// "whatever".matches("^[w-.]+@([w-]+.)+[w-]{2,4}$").
val address2: Address = ""
// Predicate isEmpty() did not fail
val person2: Person = Person(name2, email2, address2)
Compile-time
validations with
Refined
val name2: Name = ""
// Predicate isEmpty() did not fail
val email2: Email = "whatever"
// Predicate failed:
// "whatever".matches("^[w-.]+@([w-]+.)+[w-]{2,4}$").
val address2: Address = ""
// Predicate isEmpty() did not fail
val person2: Person = Person(name2, email2, address2)
Compile-time
validations with
Refined
val unknownName: Name = scala.io.StdIn.readLine()
// Compile-time refinement only works with literals
Compile-time
validations with
Refined
val unknownName: Name = scala.io.StdIn.readLine()
// Compile-time refinement only works with literals
Compile-time
validations with
Refined
val unknownName: Name = scala.io.StdIn.readLine()
// Compile-time refinement only works with literals
Runtime validations
with Refined
val name3: Either[String, Name] =
refineV(scala.io.StdIn.readLine())
val email3: Either[String, Email] =
refineV(scala.io.StdIn.readLine())
val address3: Either[String, Address] =
refineV(scala.io.StdIn.readLine())
val person3a: Either[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
Runtime validations
with Refined
val name3: Either[String, Name] =
refineV(scala.io.StdIn.readLine())
val email3: Either[String, Email] =
refineV(scala.io.StdIn.readLine())
val address3: Either[String, Address] =
refineV(scala.io.StdIn.readLine())
val person3a: Either[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
Runtime validations
with Refined
val name3: Either[String, Name] =
refineV(scala.io.StdIn.readLine())
val email3: Either[String, Email] =
refineV(scala.io.StdIn.readLine())
val address3: Either[String, Address] =
refineV(scala.io.StdIn.readLine())
val person3a: Either[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
Runtime validations
with Refined
val name3: Either[String, Name] =
refineV(scala.io.StdIn.readLine())
val email3: Either[String, Email] =
refineV(scala.io.StdIn.readLine())
val address3: Either[String, Address] =
refineV(scala.io.StdIn.readLine())
val person3a: Either[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
Runtime validations
with Refined
val name3: Either[String, Name] =
refineV(scala.io.StdIn.readLine())
val email3: Either[String, Email] =
refineV(scala.io.StdIn.readLine())
val address3: Either[String, Address] =
refineV(scala.io.StdIn.readLine())
val person3a: Either[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
Runtime validations
with Refined
val name3: Either[String, Name] =
refineV(scala.io.StdIn.readLine())
val email3: Either[String, Email] =
refineV(scala.io.StdIn.readLine())
val address3: Either[String, Address] =
refineV(scala.io.StdIn.readLine())
val person3b: Either[String, Person] =
for {
name <- address3
email <- email3
address <- name3
} yield Person(name, email, address)
Refined with Smart Constructors!
final case class Name(name: String Refined NonEmpty)
object Name {
def make(value: String): Either[String, Name] = refineV[NonEmpty](value).map(Name(_))
}
Refined with Smart Constructors!
type EmailRegex = MatchesRegex["^[w-.]+@([w-]+.)+[w-]{2,4}$"]
final case class Email(email: String Refined EmailRegex)
object Email {
def make(value: String): Either[String, Email] =
refineV[EmailRegex](value).map(Email(_))
}
Refined with Smart Constructors!
final case class Address(address: String Refined NonEmpty)
object Address {
def make(value: String): Either[String, Address] = refineV[NonEmpty](value).map(Address(_))
}
final case class Person(name: Name, email: Email, address: Address)
Refined with Smart Constructors!
final case class Address(address: String Refined NonEmpty)
object Address {
def make(value: String): Either[String, Address] = refineV[NonEmpty](value).map(Address(_))
}
final case class Person(name: Name, email: Email, address: Address)
Refined with Smart
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)
Refined with Smart
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)
Refined with Smart
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)
Refined with Smart
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)
Refined with Smart
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)
Refined with Smart
Constructors!
val name2: Either[String, Name] =
Name.make(scala.io.StdIn.readLine())
val email2: Either[String, Email] =
Email.make(scala.io.StdIn.readLine())
val address2: Either[String, Address] =
Address.make(scala.io.StdIn.readLine())
val person2a: Either[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
Refined with Smart
Constructors!
val name2: Either[String, Name] =
Name.make(scala.io.StdIn.readLine())
val email2: Either[String, Email] =
Email.make(scala.io.StdIn.readLine())
val address2: Either[String, Address] =
Address.make(scala.io.StdIn.readLine())
val person2a: Either[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
Refined with Smart
Constructors!
val name2: Either[String, Name] =
Name.make(scala.io.StdIn.readLine())
val email2: Either[String, Email] =
Email.make(scala.io.StdIn.readLine())
val address2: Either[String, Address] =
Address.make(scala.io.StdIn.readLine())
val person2a: Either[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
Refined with Smart
Constructors!
val name2: Either[String, Name] =
Name.make(scala.io.StdIn.readLine())
val email2: Either[String, Email] =
Email.make(scala.io.StdIn.readLine())
val address2: Either[String, Address] =
Address.make(scala.io.StdIn.readLine())
val person2a: Either[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
Refined with Smart
Constructors!
val name2: Either[String, Name] =
Name.make(scala.io.StdIn.readLine())
val email2: Either[String, Email] =
Email.make(scala.io.StdIn.readLine())
val address2: Either[String, Address] =
Address.make(scala.io.StdIn.readLine())
val person2a: Either[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
Refined with Smart
Constructors!
val person2b: Either[String, Person] =
for {
name <- address2 // Expected Name, found Address
email <- email2
address <- name2 // Expected Address, found Name
} yield Person(name, email, address)
Refined with Smart
Constructors!
val person2b: Either[String, Person] =
for {
name <- address2 // Expected Name, found Address
email <- email2
address <- name2 // Expected Address, found Name
} yield Person(name, email, address)
Opaque types +
Smart Constructors
(Scala 3)
opaque type Name = String
object Name:
def make(value: String): Either[String, Name] =
if value.nonEmpty then Right(value)
else Left("Name cannot be empty")
Opaque types +
Smart Constructors
(Scala 3)
opaque type Email = String
object Email:
def make(value: String): Either[String, Email] =
val regex = "^[w-.]+@([w-]+.)+[w-]{2,4}$"
if value.matches(regex) then Right(value)
else Left(s"$value does not match $regex")
Opaque types +
Smart Constructors
(Scala 3)
opaque type Address = String
object Address:
def make(value: String): Either[String, Address] =
if value.nonEmpty then Right(value)
else Left("Address cannot be empty")
final case class Person(name: Name, email: Email, address: Address)
Opaque types +
Smart Constructors
(Scala 3)
opaque type Address = String
object Address:
def make(value: String): Either[String, Address] =
if value.nonEmpty then Right(value)
else Left("Address cannot be empty")
final case class Person(name: Name, email: Email, address: Address)
Opaque types +
Smart Constructors
(Scala 3)
opaque type Address = String
object Address:
def make(value: String): Either[String, Address] =
if value.nonEmpty then Right(value)
else Left("Address cannot be empty")
final case class Person(name: Name, email: Email, address: Address)
Opaque types +
Smart Constructors
(Scala 3)
val person: 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)
Opaque types +
Smart Constructors
(Scala 3)
val person: Either[String, Person] =
for
name <- Name.make("")
email <- Email.make("whatever")
address <- Address.make("")
yield Person(name, email, address)
Wouldn't it be great if we could
have more precise data
modelling, without unnecessary
overhead at compile-time AND
at runtime?
Presenting
ZIO Prelude
Smart Types
Smart Types in Scala 2!
import zio.prelude._
import Assertion._
import Regex._
object Name extends Newtype[String] {
def assertion = assert(!isEmptyString)
}
type Name = Name.Type
Smart Types in Scala 2!
import zio.prelude._
import Assertion._
import Regex._
object Name extends Newtype[String] {
def assertion = assert(!isEmptyString)
}
type Name = Name.Type
Smart Types in Scala 2!
import zio.prelude._
import Assertion._
import Regex._
object Name extends Newtype[String] {
def assertion = assert(!isEmptyString)
}
type Name = Name.Type
Smart Types in Scala 2!
import zio.prelude._
import Assertion._
object Email extends Newtype[String] {
override def assertion = assert {
matches("^([w-.])*@([w-])*(.)*([w-]){2,4}$".r)
}
}
type Email = Email.Type
Smart Types in Scala 2!
import zio.prelude._
import Assertion._
object Email extends Newtype[String] {
override def assertion = assert {
matches("^([w-.])*@([w-])*(.)*([w-]){2,4}$".r)
}
}
type Email = Email.Type
Smart Types in Scala 2!
import zio.prelude._
import Assertion._
object Email extends Newtype[String] {
override def assertion = assert {
matches("^([w-.])*@([w-])*(.)*([w-]){2,4}$".r)
}
}
type Email = Email.Type
Smart Types in Scala 2!
import zio.prelude._
import Assertion._
import Regex._
object Email extends Newtype[String] {
override def assertion = assert {
matches {
start ~
anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~
literal("@") ~
anyRegexOf(alphanumeric, literal("-")).+ ~
literal(".").+ ~
anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~
end
}
}
}
type Email = Email.Type
Smart Types in Scala 2!
import zio.prelude._
import Assertion._
import Regex._
object Email extends Newtype[String] {
override def assertion = assert {
matches {
start ~
anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~
literal("@") ~
anyRegexOf(alphanumeric, literal("-")).+ ~
literal(".").+ ~
anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~
end
}
}
}
type Email = Email.Type
Smart Types in Scala 2!
import zio.prelude._
import Assertion._
import Regex._
object Email extends Newtype[String] {
override def assertion = assert {
matches {
start ~
anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~
literal("@") ~
anyRegexOf(alphanumeric, literal("-")).+ ~
literal(".").+ ~
anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~
end
}
}
}
type Email = Email.Type
Smart Types in Scala 2!
import zio.prelude._
import Assertion._
import Regex._
object Address extends Newtype[String] {
def assertion = assert(!isEmptyString)
}
type Address = Address.Type
Smart Types in Scala 2!
import zio.prelude._
import Assertion._
import Regex._
object Address extends Newtype[String] {
def assertion = assert(!isEmptyString)
}
type Address = Address.Type
Smart Types in Scala 2!
import zio.prelude._
import Assertion._
import Regex._
object Address extends Newtype[String] {
def assertion = assert(!isEmptyString)
}
type Address = Address.Type
Smart Types in Scala 3!
import zio.prelude.*
import Assertion.*
import Regex.*
object Name extends Newtype[String]:
override inline def assertion = !isEmptyString
type Name = Name.Type
Smart Types in Scala 3!
import zio.prelude.*
import Assertion.*
import Regex.*
object Name extends Newtype[String]:
override inline def assertion = !isEmptyString
type Name = Name.Type
Smart Types in Scala 3!
import zio.prelude.*
import Assertion.*
import Regex.*
object Name extends Newtype[String]:
override inline def assertion = !isEmptyString
type Name = Name.Type
Smart Types in Scala 3!
import zio.prelude.*
import Assertion.*
object Email extends Newtype[String]:
override inline def assertion =
matches("^([w-.])*@([w-])*(.)*([w-]){2,4}$".r)
type Email = Email.Type
Smart Types in Scala 3!
import zio.prelude.*
import Assertion.*
object Email extends Newtype[String]:
override inline def assertion =
matches("^([w-.])*@([w-])*(.)*([w-]){2,4}$".r)
type Email = Email.Type
Smart Types in Scala 3!
import zio.prelude.*
import Assertion.*
object Email extends Newtype[String]:
override inline def assertion =
matches("^([w-.])*@([w-])*(.)*([w-]){2,4}$".r)
type Email = Email.Type
Smart Types in Scala 3!
import zio.prelude.*
import Assertion.*
import Regex.*
object Email extends Newtype[String]:
override inline def assertion = matches {
start
~ anyRegexOf(alphanumeric, literal("-"), literal(".")).+
~ literal("@")
~ anyRegexOf(alphanumeric, literal("-")).+
~ literal(".").+
~ anyRegexOf(alphanumeric, literal("-")).between(2, 4)
~ end
}
type Email = Email.Type
Smart Types in Scala 3!
import zio.prelude.*
import Assertion.*
import Regex.*
object Email extends Newtype[String]:
override inline def assertion = matches {
start
~ anyRegexOf(alphanumeric, literal("-"), literal(".")).+
~ literal("@")
~ anyRegexOf(alphanumeric, literal("-")).+
~ literal(".").+
~ anyRegexOf(alphanumeric, literal("-")).between(2, 4)
~ end
}
type Email = Email.Type
Smart Types in Scala 3!
import zio.prelude.*
import Assertion.*
import Regex.*
object Email extends Newtype[String]:
override inline def assertion = matches {
start
~ anyRegexOf(alphanumeric, literal("-"), literal(".")).+
~ literal("@")
~ anyRegexOf(alphanumeric, literal("-")).+
~ literal(".").+
~ anyRegexOf(alphanumeric, literal("-")).between(2, 4)
~ end
}
type Email = Email.Type
Smart Types in Scala 3!
import zio.prelude.*
import Assertion.*
import Regex.*
object Address extends Newtype[String]:
override inline def assertion = !isEmptyString
type Address = Address.Type
Smart Types in Scala 3!
import zio.prelude.*
import Assertion.*
import Regex.*
object Address extends Newtype[String]:
override inline def assertion = !isEmptyString
type Address = Address.Type
Smart Types in Scala 3!
import zio.prelude.*
import Assertion.*
import Regex.*
object Address extends Newtype[String]:
override inline def assertion = !isEmptyString
type Address = Address.Type
Compile-time validations
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)
Compile-time validations
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)
Compile-time validations
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)
Compile-time validations
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)
Compile-time validations
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)
Compile-time validations
val name2: Name = Name("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0))
val email2: Email = Email("whatever") // COMPILATION ERROR! did not satisfy matches(^([w-.])*@([w-])*(.)*([w-]){2,4}$)
val address2: Address = Address("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0))
val person2: Person = Person(name2, email2, address2)
Compile-time validations
val name2: Name = Name("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0))
val email2: Email = Email("whatever") // COMPILATION ERROR! did not satisfy matches(^([w-.])*@([w-])*(.)*([w-]){2,4}$)
val address2: Address = Address("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0))
val person2: Person = Person(name2, email2, address2)
Compile-time validations
val name2: Name = Name("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0))
val email2: Email = Email("whatever") // COMPILATION ERROR! did not satisfy matches(^([w-.])*@([w-])*(.)*([w-]){2,4}$)
val address2: Address = Address("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0))
val person2: Person = Person(name2, email2, address2)
Compile-time validations
val name2: Name = Name("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0))
val email2: Email = Email("whatever") // COMPILATION ERROR! did not satisfy matches(^([w-.])*@([w-])*(.)*([w-]){2,4}$)
val address2: Address = Address("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0))
val person2: Person = Person(name2, email2, address2)
Compile-time validations
val name2: Name = Name("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0))
val email2: Email = Email("whatever") // COMPILATION ERROR! did not satisfy matches(^([w-.])*@([w-])*(.)*([w-]){2,4}$)
val address2: Address = Address("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0))
val person2: Person = Person(name2, email2, address2)
Compile-time validations
val unknownName: Name = Name(scala.io.StdIn.readLine())
// COMPILATION ERROR! Could not validate Assertion at compile-time
Compile-time validations
val unknownName: Name = Name(scala.io.StdIn.readLine())
// COMPILATION ERROR! Could not validate Assertion at compile-time
Compile-time validations
val unknownName: Name = Name(scala.io.StdIn.readLine())
// COMPILATION ERROR! Could not validate Assertion at compile-time
Runtime validations
val name3: Validation[String, Name] = Name.make(scala.io.StdIn.readLine())
val email3: Validation[String, Email] = Email.make(scala.io.StdIn.readLine())
val address3: Validation[String, Address] = Address.make(scala.io.StdIn.readLine())
// Short-circuiting
val person3a: Validation[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
// Non short-circuiting
val person3b: Validation[String, Person] = Validation.validateWith(name3, email3, address3)(Person.apply)
Runtime validations
val name3: Validation[String, Name] = Name.make(scala.io.StdIn.readLine())
val email3: Validation[String, Email] = Email.make(scala.io.StdIn.readLine())
val address3: Validation[String, Address] = Address.make(scala.io.StdIn.readLine())
// Short-circuiting
val person3a: Validation[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
// Non short-circuiting
val person3b: Validation[String, Person] = Validation.validateWith(name3, email3, address3)(Person.apply)
Runtime validations
val name3: Validation[String, Name] = Name.make(scala.io.StdIn.readLine())
val email3: Validation[String, Email] = Email.make(scala.io.StdIn.readLine())
val address3: Validation[String, Address] = Address.make(scala.io.StdIn.readLine())
// Short-circuiting
val person3a: Validation[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
// Non short-circuiting
val person3b: Validation[String, Person] = Validation.validateWith(name3, email3, address3)(Person.apply)
Runtime validations
val name3: Validation[String, Name] = Name.make(scala.io.StdIn.readLine())
val email3: Validation[String, Email] = Email.make(scala.io.StdIn.readLine())
val address3: Validation[String, Address] = Address.make(scala.io.StdIn.readLine())
// Short-circuiting
val person3a: Validation[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
// Non short-circuiting
val person3b: Validation[String, Person] = Validation.validateWith(name3, email3, address3)(Person.apply)
Runtime validations
val name3: Validation[String, Name] = Name.make(scala.io.StdIn.readLine())
val email3: Validation[String, Email] = Email.make(scala.io.StdIn.readLine())
val address3: Validation[String, Address] = Address.make(scala.io.StdIn.readLine())
// Short-circuiting
val person3a: Validation[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
// Non short-circuiting
val person3b: Validation[String, Person] = Validation.validateWith(name3, email3, address3)(Person.apply)
Runtime validations
val name3: Validation[String, Name] = Name.make(scala.io.StdIn.readLine())
val email3: Validation[String, Email] = Email.make(scala.io.StdIn.readLine())
val address3: Validation[String, Address] = Address.make(scala.io.StdIn.readLine())
// Short-circuiting
val person3a: Validation[String, Person] =
for {
name <- name3
email <- email3
address <- address3
} yield Person(name, email, address)
// Non short-circuiting
val person3b: Validation[String, Person] = Validation.validateWith(name3, email3, address3)(Person.apply)
Using Newtypes
// We can define methods that work with Email
def getUser(email: Email): String = Email.unwrap(email).split("@").head
val myEmail = Email("jorge.vasquez@scalac.io")
getUser(myEmail) // jorge.vasquez
Using Newtypes
// We can define methods that work with Email
def getUser(email: Email): String = Email.unwrap(email).split("@").head
val myEmail = Email("jorge.vasquez@scalac.io")
getUser(myEmail) // jorge.vasquez
Using Newtypes
// We can define methods that work with Email
def getUser(email: Email): String = Email.unwrap(email).split("@").head
val myEmail = Email("jorge.vasquez@scalac.io")
getUser(myEmail) // jorge.vasquez
Using Newtypes
// We can define methods that work with Email
def getUser(email: Email): String = Email.unwrap(email).split("@").head
val myEmail = Email("jorge.vasquez@scalac.io")
getUser(myEmail) // jorge.vasquez
Using Newtypes
def capitalize(str: String): String = str.capitalize
val myEmail = Email("jorge.vasquez@scalac.io")
capitalize(myEmail)
Using Newtypes
def capitalize(str: String): String = str.capitalize
val myEmail = Email("jorge.vasquez@scalac.io")
capitalize(myEmail)
Using Newtypes
def capitalize(str: String): String = str.capitalize
val myEmail = Email("jorge.vasquez@scalac.io")
capitalize(myEmail)
Using Newtypes
def capitalize(str: String): String = str.capitalize
val myEmail = Email("jorge.vasquez@scalac.io")
capitalize(myEmail)
Defining Subtypes in Scala 2!
import zio.prelude._
import Assertion._
import Regex._
object Email extends Subtype[String] {
override def assertion = assert {
matches {
start ~
anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~
literal("@") ~
anyRegexOf(alphanumeric, literal("-")).+ ~
literal(".").+ ~
anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~
end
}
}
}
type Email = Email.Type
Defining Subtypes in Scala 2!
import zio.prelude._
import Assertion._
import Regex._
object Email extends Subtype[String] {
override def assertion = assert {
matches {
start ~
anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~
literal("@") ~
anyRegexOf(alphanumeric, literal("-")).+ ~
literal(".").+ ~
anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~
end
}
}
}
type Email = Email.Type
Defining Subtypes in Scala 3!
import zio.prelude.*
import Assertion.*
import Regex.*
object Email extends Subtype[String]:
override inline def assertion = matches {
start ~
anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~
literal("@") ~
anyRegexOf(alphanumeric, literal("-")).+ ~
literal(".").+ ~
anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~
end
}
type Email = Email.Type
Defining Subtypes in Scala 3!
import zio.prelude.*
import Assertion.*
import Regex.*
object Email extends Subtype[String]:
override inline def assertion = matches {
start ~
anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~
literal("@") ~
anyRegexOf(alphanumeric, literal("-")).+ ~
literal(".").+ ~
anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~
end
}
type Email = Email.Type
Using Subtypes
def capitalize(str: String): String = str.capitalize
val myEmail = Email("jorge.vasquez@scalac.io")
capitalize(myEmail)
Using Subtypes
def capitalize(str: String): String = str.capitalize
val myEmail = Email("jorge.vasquez@scalac.io")
capitalize(myEmail)
Using Subtypes
def capitalize(str: String): String = str.capitalize
val myEmail = Email("jorge.vasquez@scalac.io")
capitalize(myEmail)
Using Subtypes
def capitalize(str: String): String = str.capitalize
val myEmail = Email("jorge.vasquez@scalac.io")
capitalize(myEmail)
Summary
Summary
Summary
• We need more precise data modelling for our
applications
Summary
• We need more precise data modelling for our
applications
• There are several options, each one with its own
limitations
Summary
• We need more precise data modelling for our
applications
• There are several options, each one with its own
limitations
• ZIO Prelude Smart Types provides a solution to those
limitations
Summary
Smart Const. Opaque types +
Smart Const.
Newtype Refined + Smart
Const.
ZIO Prelude
Smart Types
Supported in
Scala 2
√ √ √ √
Supported in
Scala 3
√ √ √
Bye compile-time
overhead!
√ √
Bye runtime
overhead!
√ √ √
Special thanks
Special thanks
• Ziverge for organizing this conference
Special thanks
• Ziverge for organizing this conference
• John De Goes, Kit Langton and Adam Fraser for
guidance and support
Contact me
@jorvasquez2301
jorge-vasquez-2301
jorge.vasquez@scalac.io

More Related Content

What's hot

ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaWiem Zine Elabidine
 
The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...Philip Schwarz
 
The Functional Programming Triad of Map, Filter and Fold
The Functional Programming Triad of Map, Filter and FoldThe Functional Programming Triad of Map, Filter and Fold
The Functional Programming Triad of Map, Filter and FoldPhilip Schwarz
 
Functional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 WayFunctional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 WayDebasish Ghosh
 
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018John De Goes
 
Capabilities for Resources and Effects
Capabilities for Resources and EffectsCapabilities for Resources and Effects
Capabilities for Resources and EffectsMartin Odersky
 
ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022Alexander Ioffe
 
Programación Funcional 101 con Scala y ZIO 2.0
Programación Funcional 101 con Scala y ZIO 2.0Programación Funcional 101 con Scala y ZIO 2.0
Programación Funcional 101 con Scala y ZIO 2.0Jorge Vásquez
 
Collections - Maps
Collections - Maps Collections - Maps
Collections - Maps Hitesh-Java
 
The lazy programmer's guide to writing thousands of tests
The lazy programmer's guide to writing thousands of testsThe lazy programmer's guide to writing thousands of tests
The lazy programmer's guide to writing thousands of testsScott Wlaschin
 
Why The Free Monad isn't Free
Why The Free Monad isn't FreeWhy The Free Monad isn't Free
Why The Free Monad isn't FreeKelley Robinson
 
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021Natan Silnitsky
 
Morel, a Functional Query Language
Morel, a Functional Query LanguageMorel, a Functional Query Language
Morel, a Functional Query LanguageJulian Hyde
 
Functional Error Handling with Cats
Functional Error Handling with CatsFunctional Error Handling with Cats
Functional Error Handling with CatsMark Canlas
 
Symfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsSymfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsŁukasz Chruściel
 
1. Arrow Functions | JavaScript | ES6
1. Arrow Functions | JavaScript | ES61. Arrow Functions | JavaScript | ES6
1. Arrow Functions | JavaScript | ES6pcnmtutorials
 
Functional Programming with Groovy
Functional Programming with GroovyFunctional Programming with Groovy
Functional Programming with GroovyArturo Herrero
 

What's hot (20)

ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in Scala
 
The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...
 
Introduction to kotlin
Introduction to kotlinIntroduction to kotlin
Introduction to kotlin
 
The Functional Programming Triad of Map, Filter and Fold
The Functional Programming Triad of Map, Filter and FoldThe Functional Programming Triad of Map, Filter and Fold
The Functional Programming Triad of Map, Filter and Fold
 
Functional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 WayFunctional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 Way
 
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
 
Capabilities for Resources and Effects
Capabilities for Resources and EffectsCapabilities for Resources and Effects
Capabilities for Resources and Effects
 
ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022
 
Programación Funcional 101 con Scala y ZIO 2.0
Programación Funcional 101 con Scala y ZIO 2.0Programación Funcional 101 con Scala y ZIO 2.0
Programación Funcional 101 con Scala y ZIO 2.0
 
Collections - Maps
Collections - Maps Collections - Maps
Collections - Maps
 
The lazy programmer's guide to writing thousands of tests
The lazy programmer's guide to writing thousands of testsThe lazy programmer's guide to writing thousands of tests
The lazy programmer's guide to writing thousands of tests
 
Why The Free Monad isn't Free
Why The Free Monad isn't FreeWhy The Free Monad isn't Free
Why The Free Monad isn't Free
 
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
 
Morel, a Functional Query Language
Morel, a Functional Query LanguageMorel, a Functional Query Language
Morel, a Functional Query Language
 
Functional Error Handling with Cats
Functional Error Handling with CatsFunctional Error Handling with Cats
Functional Error Handling with Cats
 
Preparing for Scala 3
Preparing for Scala 3Preparing for Scala 3
Preparing for Scala 3
 
Symfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsSymfony World - Symfony components and design patterns
Symfony World - Symfony components and design patterns
 
How to Use JSON in MySQL Wrong
How to Use JSON in MySQL WrongHow to Use JSON in MySQL Wrong
How to Use JSON in MySQL Wrong
 
1. Arrow Functions | JavaScript | ES6
1. Arrow Functions | JavaScript | ES61. Arrow Functions | JavaScript | ES6
1. Arrow Functions | JavaScript | ES6
 
Functional Programming with Groovy
Functional Programming with GroovyFunctional Programming with Groovy
Functional Programming with Groovy
 

Similar to Constrain Types Free Your Brain

ZIO Prelude - ZIO World 2021
ZIO Prelude - ZIO World 2021ZIO Prelude - ZIO World 2021
ZIO Prelude - ZIO World 2021Jorge Vásquez
 
Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2Kirill Rozov
 
Scala - en bedre og mere effektiv Java?
Scala - en bedre og mere effektiv Java?Scala - en bedre og mere effektiv Java?
Scala - en bedre og mere effektiv Java?Jesper Kamstrup Linnet
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
beginners_python_cheat_sheet_pcc_all (3).pptx
beginners_python_cheat_sheet_pcc_all (3).pptxbeginners_python_cheat_sheet_pcc_all (3).pptx
beginners_python_cheat_sheet_pcc_all (3).pptxHongAnhNguyn285885
 
Beginner's Python Cheat Sheet.pdf
Beginner's Python Cheat Sheet.pdfBeginner's Python Cheat Sheet.pdf
Beginner's Python Cheat Sheet.pdfAkhileshKumar436707
 
(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?Tomasz Wrobel
 
Scala in a Java 8 World
Scala in a Java 8 WorldScala in a Java 8 World
Scala in a Java 8 WorldDaniel Blyth
 
Groovy Ast Transformations (greach)
Groovy Ast Transformations (greach)Groovy Ast Transformations (greach)
Groovy Ast Transformations (greach)HamletDRC
 
Basics of Python programming (part 2)
Basics of Python programming (part 2)Basics of Python programming (part 2)
Basics of Python programming (part 2)Pedro Rodrigues
 
python cheat sheat, Data science, Machine learning
python cheat sheat, Data science, Machine learningpython cheat sheat, Data science, Machine learning
python cheat sheat, Data science, Machine learningTURAGAVIJAYAAKASH
 
Beginner's Python Cheat Sheet
Beginner's Python Cheat SheetBeginner's Python Cheat Sheet
Beginner's Python Cheat SheetVerxus
 

Similar to Constrain Types Free Your Brain (20)

ZIO Prelude - ZIO World 2021
ZIO Prelude - ZIO World 2021ZIO Prelude - ZIO World 2021
ZIO Prelude - ZIO World 2021
 
Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2
 
Scala - en bedre og mere effektiv Java?
Scala - en bedre og mere effektiv Java?Scala - en bedre og mere effektiv Java?
Scala - en bedre og mere effektiv Java?
 
Scala - en bedre Java?
Scala - en bedre Java?Scala - en bedre Java?
Scala - en bedre Java?
 
Benefits of Kotlin
Benefits of KotlinBenefits of Kotlin
Benefits of Kotlin
 
Introduction to Scala
Introduction to ScalaIntroduction to Scala
Introduction to Scala
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
Java string handling
Java string handlingJava string handling
Java string handling
 
beginners_python_cheat_sheet_pcc_all (3).pptx
beginners_python_cheat_sheet_pcc_all (3).pptxbeginners_python_cheat_sheet_pcc_all (3).pptx
beginners_python_cheat_sheet_pcc_all (3).pptx
 
Beginner's Python Cheat Sheet.pdf
Beginner's Python Cheat Sheet.pdfBeginner's Python Cheat Sheet.pdf
Beginner's Python Cheat Sheet.pdf
 
(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?
 
Scala in a Java 8 World
Scala in a Java 8 WorldScala in a Java 8 World
Scala in a Java 8 World
 
Groovy Ast Transformations (greach)
Groovy Ast Transformations (greach)Groovy Ast Transformations (greach)
Groovy Ast Transformations (greach)
 
Basics of Python programming (part 2)
Basics of Python programming (part 2)Basics of Python programming (part 2)
Basics of Python programming (part 2)
 
2. Python Cheat Sheet.pdf
2. Python Cheat Sheet.pdf2. Python Cheat Sheet.pdf
2. Python Cheat Sheet.pdf
 
python cheat sheat, Data science, Machine learning
python cheat sheat, Data science, Machine learningpython cheat sheat, Data science, Machine learning
python cheat sheat, Data science, Machine learning
 
Beginner's Python Cheat Sheet
Beginner's Python Cheat SheetBeginner's Python Cheat Sheet
Beginner's Python Cheat Sheet
 

More from Jorge Vásquez

Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!
Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!
Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!Jorge Vásquez
 
Consiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIO
Consiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIOConsiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIO
Consiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIOJorge Vásquez
 
Functional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorldFunctional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorldJorge Vásquez
 
Exploring type level programming in Scala
Exploring type level programming in ScalaExploring type level programming in Scala
Exploring type level programming in ScalaJorge Vásquez
 
The Terror-Free Guide to Introducing Functional Scala at Work
The Terror-Free Guide to Introducing Functional Scala at WorkThe Terror-Free Guide to Introducing Functional Scala at Work
The Terror-Free Guide to Introducing Functional Scala at WorkJorge Vásquez
 
Introduction to programming with ZIO functional effects
Introduction to programming with ZIO functional effectsIntroduction to programming with ZIO functional effects
Introduction to programming with ZIO functional effectsJorge Vásquez
 

More from Jorge Vásquez (6)

Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!
Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!
Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!
 
Consiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIO
Consiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIOConsiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIO
Consiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIO
 
Functional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorldFunctional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorld
 
Exploring type level programming in Scala
Exploring type level programming in ScalaExploring type level programming in Scala
Exploring type level programming in Scala
 
The Terror-Free Guide to Introducing Functional Scala at Work
The Terror-Free Guide to Introducing Functional Scala at WorkThe Terror-Free Guide to Introducing Functional Scala at Work
The Terror-Free Guide to Introducing Functional Scala at Work
 
Introduction to programming with ZIO functional effects
Introduction to programming with ZIO functional effectsIntroduction to programming with ZIO functional effects
Introduction to programming with ZIO functional effects
 

Recently uploaded

2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdf
2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdf2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdf
2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdfAndrey Devyatkin
 
Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...
Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...
Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...OnePlan Solutions
 
Large Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLarge Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLionel Briand
 
Strategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero resultsStrategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero resultsJean Silva
 
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptxThe Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptxRTS corp
 
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full RecordingOpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full RecordingShane Coughlan
 
eSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration toolseSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration toolsosttopstonverter
 
The Ultimate Guide to Performance Testing in Low-Code, No-Code Environments (...
The Ultimate Guide to Performance Testing in Low-Code, No-Code Environments (...The Ultimate Guide to Performance Testing in Low-Code, No-Code Environments (...
The Ultimate Guide to Performance Testing in Low-Code, No-Code Environments (...kalichargn70th171
 
Ronisha Informatics Private Limited Catalogue
Ronisha Informatics Private Limited CatalogueRonisha Informatics Private Limited Catalogue
Ronisha Informatics Private Limited Catalogueitservices996
 
Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecturerahul_net
 
Mastering Project Planning with Microsoft Project 2016.pptx
Mastering Project Planning with Microsoft Project 2016.pptxMastering Project Planning with Microsoft Project 2016.pptx
Mastering Project Planning with Microsoft Project 2016.pptxAS Design & AST.
 
Pros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdf
Pros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdfPros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdf
Pros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdfkalichargn70th171
 
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdfEnhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdfRTS corp
 
Understanding Plagiarism: Causes, Consequences and Prevention.pptx
Understanding Plagiarism: Causes, Consequences and Prevention.pptxUnderstanding Plagiarism: Causes, Consequences and Prevention.pptx
Understanding Plagiarism: Causes, Consequences and Prevention.pptxSasikiranMarri
 
Best Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh ITBest Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh ITmanoharjgpsolutions
 
SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?Alexandre Beguel
 
What’s New in VictoriaMetrics: Q1 2024 Updates
What’s New in VictoriaMetrics: Q1 2024 UpdatesWhat’s New in VictoriaMetrics: Q1 2024 Updates
What’s New in VictoriaMetrics: Q1 2024 UpdatesVictoriaMetrics
 
[ CNCF Q1 2024 ] Intro to Continuous Profiling and Grafana Pyroscope.pdf
[ CNCF Q1 2024 ] Intro to Continuous Profiling and Grafana Pyroscope.pdf[ CNCF Q1 2024 ] Intro to Continuous Profiling and Grafana Pyroscope.pdf
[ CNCF Q1 2024 ] Intro to Continuous Profiling and Grafana Pyroscope.pdfSteve Caron
 
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...OnePlan Solutions
 
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...Bert Jan Schrijver
 

Recently uploaded (20)

2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdf
2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdf2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdf
2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdf
 
Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...
Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...
Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...
 
Large Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLarge Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and Repair
 
Strategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero resultsStrategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero results
 
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptxThe Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
 
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full RecordingOpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
 
eSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration toolseSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration tools
 
The Ultimate Guide to Performance Testing in Low-Code, No-Code Environments (...
The Ultimate Guide to Performance Testing in Low-Code, No-Code Environments (...The Ultimate Guide to Performance Testing in Low-Code, No-Code Environments (...
The Ultimate Guide to Performance Testing in Low-Code, No-Code Environments (...
 
Ronisha Informatics Private Limited Catalogue
Ronisha Informatics Private Limited CatalogueRonisha Informatics Private Limited Catalogue
Ronisha Informatics Private Limited Catalogue
 
Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecture
 
Mastering Project Planning with Microsoft Project 2016.pptx
Mastering Project Planning with Microsoft Project 2016.pptxMastering Project Planning with Microsoft Project 2016.pptx
Mastering Project Planning with Microsoft Project 2016.pptx
 
Pros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdf
Pros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdfPros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdf
Pros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdf
 
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdfEnhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
 
Understanding Plagiarism: Causes, Consequences and Prevention.pptx
Understanding Plagiarism: Causes, Consequences and Prevention.pptxUnderstanding Plagiarism: Causes, Consequences and Prevention.pptx
Understanding Plagiarism: Causes, Consequences and Prevention.pptx
 
Best Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh ITBest Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh IT
 
SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?
 
What’s New in VictoriaMetrics: Q1 2024 Updates
What’s New in VictoriaMetrics: Q1 2024 UpdatesWhat’s New in VictoriaMetrics: Q1 2024 Updates
What’s New in VictoriaMetrics: Q1 2024 Updates
 
[ CNCF Q1 2024 ] Intro to Continuous Profiling and Grafana Pyroscope.pdf
[ CNCF Q1 2024 ] Intro to Continuous Profiling and Grafana Pyroscope.pdf[ CNCF Q1 2024 ] Intro to Continuous Profiling and Grafana Pyroscope.pdf
[ CNCF Q1 2024 ] Intro to Continuous Profiling and Grafana Pyroscope.pdf
 
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
 
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
 

Constrain Types Free Your Brain

  • 1. Be Smart, Constrain Your Types to Free Your Brain! Functional Scala December 3rd, 2021
  • 3. Background ZIO Prelude is a Scala-first take on functional abstractions
  • 4. Background ZIO Prelude is a Scala-first take on functional abstractions • Type classes to describe the ways different types are similar
  • 5. Background ZIO Prelude is a Scala-first take on functional abstractions • Type classes to describe the ways different types are similar • Data types that complement the Scala standard library (NonEmptyList, ZValidation, ZPure)
  • 6. Background ZIO Prelude is a Scala-first take on functional abstractions • Type classes to describe the ways different types are similar • Data types that complement the Scala standard library (NonEmptyList, ZValidation, ZPure) • Smart Types
  • 8. Representing everything with Simple Types final case class Person( name: String, email: String, address: String )
  • 9. Representing everything with Simple Types final case class Person( name: String, email: String, address: String )
  • 10. Representing everything with Simple Types final case class Person( name: String, email: String, address: String )
  • 11. Representing everything with Simple Types final case class Person( name: String, email: String, address: String )
  • 12. Representing everything with Simple Types final case class Person( name: String, email: String, address: String )
  • 13. Storing invalid data in databases name email address t1@tst.co whatever Main Av. t2@tst.co
  • 16.
  • 17. Smart Constructors final case class Name private (name: String) object Name { def make(value: String): Either[String, Name] = if (value.nonEmpty) Right(Name(value)) else Left("Name cannot be empty") }
  • 18. Smart Constructors final case class Name private (name: String) object Name { def make(value: String): Either[String, Name] = if (value.nonEmpty) Right(Name(value)) else Left("Name cannot be empty") }
  • 19. Smart Constructors final case class Name private (name: String) object Name { def make(value: String): Either[String, Name] = if (value.nonEmpty) Right(Name(value)) else Left("Name cannot be empty") }
  • 20. Smart Constructors final case class Name private (name: String) object Name { def make(value: String): Either[String, Name] = if (value.nonEmpty) Right(Name(value)) else Left("Name cannot be empty") }
  • 21. Smart Constructors final case class Email private (email: String) object Email { def make(value: String): Either[String, Email] = { val regex = "^[w-.]+@([w-]+.)+[w-]{2,4}$" if (value.matches(regex)) Right(Email(value)) else Left(s"$value does not match $regex") } }
  • 22. Smart Constructors final case class Address private (address: String) object Address { def make(value: String): Either[String, Address] = if (value.nonEmpty) Right(Address(value)) else Left("Address cannot be empty") } final case class Person(name: Name, email: Email, address: Address)
  • 23. Smart Constructors final case class Address private (address: String) object Address { def make(value: String): Either[String, Address] = if (value.nonEmpty) Right(Address(value)) else Left("Address cannot be empty") } final case class Person(name: Name, email: Email, address: Address)
  • 24. Smart Constructors final case class Address private (address: String) object Address { def make(value: String): Either[String, Address] = if (value.nonEmpty) Right(Address(value)) else Left("Address cannot be empty") } final case class Person(name: Name, email: Email, address: Address)
  • 25.
  • 26.
  • 27. Smart Constructors val person: 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)
  • 28. Smart Constructors val person: 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)
  • 29. Smart Constructors val person: 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)
  • 30. Smart Constructors val person: 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)
  • 31. Smart Constructors val person: 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)
  • 32. Smart Constructors val person: 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)
  • 33. Smart Constructors val person: Either[String, Person] = for { name <- Name.make("") email <- Email.make("whatever") address <- Address.make("") } yield Person(name, email, address)
  • 34. Smart Constructors val person: Either[String, Person] = for { name <- Name.make("") email <- Email.make("whatever") address <- Address.make("") } yield Person(name, email, address)
  • 35. Smart Constructors val person: Either[String, Person] = for { name <- Name.make("") email <- Email.make("whatever") address <- Address.make("") } yield Person(name, email, address)
  • 36. Smart Constructors val person: Either[String, Person] = for { name <- Name.make("") email <- Email.make("whatever") address <- Address.make("") } yield Person(name, email, address)
  • 37. Smart Constructors val person: Either[String, Person] = for { name <- Name.make("") email <- Email.make("whatever") address <- Address.make("") } yield Person(name, email, address)
  • 38. Smart Constructors val person: Either[String, Person] = for { name <- Name.make("") email <- Email.make("whatever") address <- Address.make("") } yield Person(name, email, address)
  • 39.
  • 40. Using the Newtype library import io.estatico.newtype.macros.newtype import io.estatico.newtype.ops._ @newtype class Name(name: String) object Name { def make(value: String): Either[String, Name] = if (value.nonEmpty) Right(value.coerce) else Left("Name cannot be empty") }
  • 41. Using the Newtype library import io.estatico.newtype.macros.newtype import io.estatico.newtype.ops._ @newtype class Email(email: String) object Email { def make(value: String): Either[String, Email] = { val regex = "^[w-.]+@([w-]+.)+[w-]{2,4}$" if (value.matches(regex)) Right(value.coerce) else Left(s"$value does not match $regex") } }
  • 42. Using the Newtype library import io.estatico.newtype.macros.newtype import io.estatico.newtype.ops._ @newtype class Address(address: String) object Address { def make(value: String): Either[String, Address] = if (value.nonEmpty) Right(value.coerce) else Left("Address cannot be empty") } final case class Person(name: Name, email: Email, address: Address)
  • 43. Using the Newtype library import io.estatico.newtype.macros.newtype import io.estatico.newtype.ops._ @newtype class Address(address: String) object Address { def make(value: String): Either[String, Address] = if (value.nonEmpty) Right(value.coerce) else Left("Address cannot be empty") } final case class Person(name: Name, email: Email, address: Address)
  • 44. Using the Newtype library import io.estatico.newtype.macros.newtype import io.estatico.newtype.ops._ @newtype class Address(address: String) object Address { def make(value: String): Either[String, Address] = if (value.nonEmpty) Right(value.coerce) else Left("Address cannot be empty") } final case class Person(name: Name, email: Email, address: Address)
  • 45.
  • 46. Newtype library val person: 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)
  • 47. Newtype library val person: Either[String, Person] = for { name <- Name.make("") email <- Email.make("whatever") address <- Address.make("") } yield Person(name, email, address)
  • 48.
  • 49. Using the Refined library import eu.timepit.refined._ import eu.timepit.refined.api.Refined import eu.timepit.refined.auto._ import eu.timepit.refined.collection._ import eu.timepit.refined.string._ type Name = String Refined NonEmpty type Email = String Refined MatchesRegex["^[w-.]+@([w-]+.)+[w-]{2,4}$"] // This works since Scala 2.13 thanks to literal types type Email = String Refined MatchesRegex[W.`^[w-.]+@([w-]+.)+[w-]{2,4}$`.T] // Before Scala 2.13 type Address = String Refined NonEmpty final case class Person(name: Name, email: Email, address: Address)
  • 50. Using the Refined library import eu.timepit.refined._ import eu.timepit.refined.api.Refined import eu.timepit.refined.auto._ import eu.timepit.refined.collection._ import eu.timepit.refined.string._ type Name = String Refined NonEmpty type Email = String Refined MatchesRegex["^[w-.]+@([w-]+.)+[w-]{2,4}$"] // This works since Scala 2.13 thanks to literal types type Email = String Refined MatchesRegex[W.`^[w-.]+@([w-]+.)+[w-]{2,4}$`.T] // Before Scala 2.13 type Address = String Refined NonEmpty final case class Person(name: Name, email: Email, address: Address)
  • 51. Using the Refined library import eu.timepit.refined._ import eu.timepit.refined.api.Refined import eu.timepit.refined.auto._ import eu.timepit.refined.collection._ import eu.timepit.refined.string._ type Name = String Refined NonEmpty type Email = String Refined MatchesRegex["^[w-.]+@([w-]+.)+[w-]{2,4}$"] // This works since Scala 2.13 thanks to literal types type Email = String Refined MatchesRegex[W.`^[w-.]+@([w-]+.)+[w-]{2,4}$`.T] // Before Scala 2.13 type Address = String Refined NonEmpty final case class Person(name: Name, email: Email, address: Address)
  • 52. Using the Refined library import eu.timepit.refined._ import eu.timepit.refined.api.Refined import eu.timepit.refined.auto._ import eu.timepit.refined.collection._ import eu.timepit.refined.string._ type Name = String Refined NonEmpty type Email = String Refined MatchesRegex["^[w-.]+@([w-]+.)+[w-]{2,4}$"] // This works since Scala 2.13 thanks to literal types type Email = String Refined MatchesRegex[W.`^[w-.]+@([w-]+.)+[w-]{2,4}$`.T] // Before Scala 2.13 type Address = String Refined NonEmpty final case class Person(name: Name, email: Email, address: Address)
  • 53. Using the Refined library import eu.timepit.refined._ import eu.timepit.refined.api.Refined import eu.timepit.refined.auto._ import eu.timepit.refined.collection._ import eu.timepit.refined.string._ type Name = String Refined NonEmpty type Email = String Refined MatchesRegex["^[w-.]+@([w-]+.)+[w-]{2,4}$"] // This works since Scala 2.13 thanks to literal types type Email = String Refined MatchesRegex[W.`^[w-.]+@([w-]+.)+[w-]{2,4}$`.T] // Before Scala 2.13 type Address = String Refined NonEmpty final case class Person(name: Name, email: Email, address: Address)
  • 54. Using the Refined library import eu.timepit.refined._ import eu.timepit.refined.api.Refined import eu.timepit.refined.auto._ import eu.timepit.refined.collection._ import eu.timepit.refined.string._ type Name = String Refined NonEmpty type Email = String Refined MatchesRegex["^[w-.]+@([w-]+.)+[w-]{2,4}$"] // This works since Scala 2.13 thanks to literal types type Email = String Refined MatchesRegex[W.`^[w-.]+@([w-]+.)+[w-]{2,4}$`.T] // Before Scala 2.13 type Address = String Refined NonEmpty final case class Person(name: Name, email: Email, address: Address)
  • 55. Compile-time validations with Refined val name1: Name = "Jorge" val email1: Email = "jorge.vasquez@scalac.io" val address1: Address = "100 Some St." val person1: Person = Person(name1, email1, address1)
  • 56. Compile-time validations with Refined val name1: Name = "Jorge" val email1: Email = "jorge.vasquez@scalac.io" val address1: Address = "100 Some St." val person1: Person = Person(name1, email1, address1)
  • 57. Compile-time validations with Refined val name1: Name = "Jorge" val email1: Email = "jorge.vasquez@scalac.io" val address1: Address = "100 Some St." val person1: Person = Person(name1, email1, address1)
  • 58. Compile-time validations with Refined val name1: Name = "Jorge" val email1: Email = "jorge.vasquez@scalac.io" val address1: Address = "100 Some St." val person1: Person = Person(name1, email1, address1)
  • 59. Compile-time validations with Refined val name1: Name = "Jorge" val email1: Email = "jorge.vasquez@scalac.io" val address1: Address = "100 Some St." val person1: Person = Person(name1, email1, address1)
  • 60. Compile-time validations with Refined val name2: Name = "" // Predicate isEmpty() did not fail val email2: Email = "whatever" // Predicate failed: // "whatever".matches("^[w-.]+@([w-]+.)+[w-]{2,4}$"). val address2: Address = "" // Predicate isEmpty() did not fail val person2: Person = Person(name2, email2, address2)
  • 61. Compile-time validations with Refined val name2: Name = "" // Predicate isEmpty() did not fail val email2: Email = "whatever" // Predicate failed: // "whatever".matches("^[w-.]+@([w-]+.)+[w-]{2,4}$"). val address2: Address = "" // Predicate isEmpty() did not fail val person2: Person = Person(name2, email2, address2)
  • 62. Compile-time validations with Refined val name2: Name = "" // Predicate isEmpty() did not fail val email2: Email = "whatever" // Predicate failed: // "whatever".matches("^[w-.]+@([w-]+.)+[w-]{2,4}$"). val address2: Address = "" // Predicate isEmpty() did not fail val person2: Person = Person(name2, email2, address2)
  • 63. Compile-time validations with Refined val name2: Name = "" // Predicate isEmpty() did not fail val email2: Email = "whatever" // Predicate failed: // "whatever".matches("^[w-.]+@([w-]+.)+[w-]{2,4}$"). val address2: Address = "" // Predicate isEmpty() did not fail val person2: Person = Person(name2, email2, address2)
  • 64. Compile-time validations with Refined val name2: Name = "" // Predicate isEmpty() did not fail val email2: Email = "whatever" // Predicate failed: // "whatever".matches("^[w-.]+@([w-]+.)+[w-]{2,4}$"). val address2: Address = "" // Predicate isEmpty() did not fail val person2: Person = Person(name2, email2, address2)
  • 65. Compile-time validations with Refined val unknownName: Name = scala.io.StdIn.readLine() // Compile-time refinement only works with literals
  • 66. Compile-time validations with Refined val unknownName: Name = scala.io.StdIn.readLine() // Compile-time refinement only works with literals
  • 67. Compile-time validations with Refined val unknownName: Name = scala.io.StdIn.readLine() // Compile-time refinement only works with literals
  • 68. Runtime validations with Refined val name3: Either[String, Name] = refineV(scala.io.StdIn.readLine()) val email3: Either[String, Email] = refineV(scala.io.StdIn.readLine()) val address3: Either[String, Address] = refineV(scala.io.StdIn.readLine()) val person3a: Either[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address)
  • 69. Runtime validations with Refined val name3: Either[String, Name] = refineV(scala.io.StdIn.readLine()) val email3: Either[String, Email] = refineV(scala.io.StdIn.readLine()) val address3: Either[String, Address] = refineV(scala.io.StdIn.readLine()) val person3a: Either[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address)
  • 70. Runtime validations with Refined val name3: Either[String, Name] = refineV(scala.io.StdIn.readLine()) val email3: Either[String, Email] = refineV(scala.io.StdIn.readLine()) val address3: Either[String, Address] = refineV(scala.io.StdIn.readLine()) val person3a: Either[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address)
  • 71. Runtime validations with Refined val name3: Either[String, Name] = refineV(scala.io.StdIn.readLine()) val email3: Either[String, Email] = refineV(scala.io.StdIn.readLine()) val address3: Either[String, Address] = refineV(scala.io.StdIn.readLine()) val person3a: Either[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address)
  • 72. Runtime validations with Refined val name3: Either[String, Name] = refineV(scala.io.StdIn.readLine()) val email3: Either[String, Email] = refineV(scala.io.StdIn.readLine()) val address3: Either[String, Address] = refineV(scala.io.StdIn.readLine()) val person3a: Either[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address)
  • 73. Runtime validations with Refined val name3: Either[String, Name] = refineV(scala.io.StdIn.readLine()) val email3: Either[String, Email] = refineV(scala.io.StdIn.readLine()) val address3: Either[String, Address] = refineV(scala.io.StdIn.readLine()) val person3b: Either[String, Person] = for { name <- address3 email <- email3 address <- name3 } yield Person(name, email, address)
  • 74.
  • 75. Refined with Smart Constructors! final case class Name(name: String Refined NonEmpty) object Name { def make(value: String): Either[String, Name] = refineV[NonEmpty](value).map(Name(_)) }
  • 76. Refined with Smart Constructors! type EmailRegex = MatchesRegex["^[w-.]+@([w-]+.)+[w-]{2,4}$"] final case class Email(email: String Refined EmailRegex) object Email { def make(value: String): Either[String, Email] = refineV[EmailRegex](value).map(Email(_)) }
  • 77. Refined with Smart Constructors! final case class Address(address: String Refined NonEmpty) object Address { def make(value: String): Either[String, Address] = refineV[NonEmpty](value).map(Address(_)) } final case class Person(name: Name, email: Email, address: Address)
  • 78. Refined with Smart Constructors! final case class Address(address: String Refined NonEmpty) object Address { def make(value: String): Either[String, Address] = refineV[NonEmpty](value).map(Address(_)) } final case class Person(name: Name, email: Email, address: Address)
  • 79. Refined with Smart 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)
  • 80. Refined with Smart 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)
  • 81. Refined with Smart 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)
  • 82. Refined with Smart 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)
  • 83. Refined with Smart 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)
  • 84. Refined with Smart Constructors! val name2: Either[String, Name] = Name.make(scala.io.StdIn.readLine()) val email2: Either[String, Email] = Email.make(scala.io.StdIn.readLine()) val address2: Either[String, Address] = Address.make(scala.io.StdIn.readLine()) val person2a: Either[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address)
  • 85. Refined with Smart Constructors! val name2: Either[String, Name] = Name.make(scala.io.StdIn.readLine()) val email2: Either[String, Email] = Email.make(scala.io.StdIn.readLine()) val address2: Either[String, Address] = Address.make(scala.io.StdIn.readLine()) val person2a: Either[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address)
  • 86. Refined with Smart Constructors! val name2: Either[String, Name] = Name.make(scala.io.StdIn.readLine()) val email2: Either[String, Email] = Email.make(scala.io.StdIn.readLine()) val address2: Either[String, Address] = Address.make(scala.io.StdIn.readLine()) val person2a: Either[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address)
  • 87. Refined with Smart Constructors! val name2: Either[String, Name] = Name.make(scala.io.StdIn.readLine()) val email2: Either[String, Email] = Email.make(scala.io.StdIn.readLine()) val address2: Either[String, Address] = Address.make(scala.io.StdIn.readLine()) val person2a: Either[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address)
  • 88. Refined with Smart Constructors! val name2: Either[String, Name] = Name.make(scala.io.StdIn.readLine()) val email2: Either[String, Email] = Email.make(scala.io.StdIn.readLine()) val address2: Either[String, Address] = Address.make(scala.io.StdIn.readLine()) val person2a: Either[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address)
  • 89. Refined with Smart Constructors! val person2b: Either[String, Person] = for { name <- address2 // Expected Name, found Address email <- email2 address <- name2 // Expected Address, found Name } yield Person(name, email, address)
  • 90. Refined with Smart Constructors! val person2b: Either[String, Person] = for { name <- address2 // Expected Name, found Address email <- email2 address <- name2 // Expected Address, found Name } yield Person(name, email, address)
  • 91.
  • 92.
  • 93.
  • 94. Opaque types + Smart Constructors (Scala 3) opaque type Name = String object Name: def make(value: String): Either[String, Name] = if value.nonEmpty then Right(value) else Left("Name cannot be empty")
  • 95. Opaque types + Smart Constructors (Scala 3) opaque type Email = String object Email: def make(value: String): Either[String, Email] = val regex = "^[w-.]+@([w-]+.)+[w-]{2,4}$" if value.matches(regex) then Right(value) else Left(s"$value does not match $regex")
  • 96. Opaque types + Smart Constructors (Scala 3) opaque type Address = String object Address: def make(value: String): Either[String, Address] = if value.nonEmpty then Right(value) else Left("Address cannot be empty") final case class Person(name: Name, email: Email, address: Address)
  • 97. Opaque types + Smart Constructors (Scala 3) opaque type Address = String object Address: def make(value: String): Either[String, Address] = if value.nonEmpty then Right(value) else Left("Address cannot be empty") final case class Person(name: Name, email: Email, address: Address)
  • 98. Opaque types + Smart Constructors (Scala 3) opaque type Address = String object Address: def make(value: String): Either[String, Address] = if value.nonEmpty then Right(value) else Left("Address cannot be empty") final case class Person(name: Name, email: Email, address: Address)
  • 99.
  • 100. Opaque types + Smart Constructors (Scala 3) val person: 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)
  • 101. Opaque types + Smart Constructors (Scala 3) val person: Either[String, Person] = for name <- Name.make("") email <- Email.make("whatever") address <- Address.make("") yield Person(name, email, address)
  • 102.
  • 103.
  • 104. Wouldn't it be great if we could have more precise data modelling, without unnecessary overhead at compile-time AND at runtime?
  • 106. Smart Types in Scala 2! import zio.prelude._ import Assertion._ import Regex._ object Name extends Newtype[String] { def assertion = assert(!isEmptyString) } type Name = Name.Type
  • 107. Smart Types in Scala 2! import zio.prelude._ import Assertion._ import Regex._ object Name extends Newtype[String] { def assertion = assert(!isEmptyString) } type Name = Name.Type
  • 108. Smart Types in Scala 2! import zio.prelude._ import Assertion._ import Regex._ object Name extends Newtype[String] { def assertion = assert(!isEmptyString) } type Name = Name.Type
  • 109. Smart Types in Scala 2! import zio.prelude._ import Assertion._ object Email extends Newtype[String] { override def assertion = assert { matches("^([w-.])*@([w-])*(.)*([w-]){2,4}$".r) } } type Email = Email.Type
  • 110. Smart Types in Scala 2! import zio.prelude._ import Assertion._ object Email extends Newtype[String] { override def assertion = assert { matches("^([w-.])*@([w-])*(.)*([w-]){2,4}$".r) } } type Email = Email.Type
  • 111. Smart Types in Scala 2! import zio.prelude._ import Assertion._ object Email extends Newtype[String] { override def assertion = assert { matches("^([w-.])*@([w-])*(.)*([w-]){2,4}$".r) } } type Email = Email.Type
  • 112. Smart Types in Scala 2! import zio.prelude._ import Assertion._ import Regex._ object Email extends Newtype[String] { override def assertion = assert { matches { start ~ anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~ literal("@") ~ anyRegexOf(alphanumeric, literal("-")).+ ~ literal(".").+ ~ anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~ end } } } type Email = Email.Type
  • 113. Smart Types in Scala 2! import zio.prelude._ import Assertion._ import Regex._ object Email extends Newtype[String] { override def assertion = assert { matches { start ~ anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~ literal("@") ~ anyRegexOf(alphanumeric, literal("-")).+ ~ literal(".").+ ~ anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~ end } } } type Email = Email.Type
  • 114. Smart Types in Scala 2! import zio.prelude._ import Assertion._ import Regex._ object Email extends Newtype[String] { override def assertion = assert { matches { start ~ anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~ literal("@") ~ anyRegexOf(alphanumeric, literal("-")).+ ~ literal(".").+ ~ anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~ end } } } type Email = Email.Type
  • 115. Smart Types in Scala 2! import zio.prelude._ import Assertion._ import Regex._ object Address extends Newtype[String] { def assertion = assert(!isEmptyString) } type Address = Address.Type
  • 116. Smart Types in Scala 2! import zio.prelude._ import Assertion._ import Regex._ object Address extends Newtype[String] { def assertion = assert(!isEmptyString) } type Address = Address.Type
  • 117. Smart Types in Scala 2! import zio.prelude._ import Assertion._ import Regex._ object Address extends Newtype[String] { def assertion = assert(!isEmptyString) } type Address = Address.Type
  • 118. Smart Types in Scala 3! import zio.prelude.* import Assertion.* import Regex.* object Name extends Newtype[String]: override inline def assertion = !isEmptyString type Name = Name.Type
  • 119. Smart Types in Scala 3! import zio.prelude.* import Assertion.* import Regex.* object Name extends Newtype[String]: override inline def assertion = !isEmptyString type Name = Name.Type
  • 120. Smart Types in Scala 3! import zio.prelude.* import Assertion.* import Regex.* object Name extends Newtype[String]: override inline def assertion = !isEmptyString type Name = Name.Type
  • 121. Smart Types in Scala 3! import zio.prelude.* import Assertion.* object Email extends Newtype[String]: override inline def assertion = matches("^([w-.])*@([w-])*(.)*([w-]){2,4}$".r) type Email = Email.Type
  • 122. Smart Types in Scala 3! import zio.prelude.* import Assertion.* object Email extends Newtype[String]: override inline def assertion = matches("^([w-.])*@([w-])*(.)*([w-]){2,4}$".r) type Email = Email.Type
  • 123. Smart Types in Scala 3! import zio.prelude.* import Assertion.* object Email extends Newtype[String]: override inline def assertion = matches("^([w-.])*@([w-])*(.)*([w-]){2,4}$".r) type Email = Email.Type
  • 124. Smart Types in Scala 3! import zio.prelude.* import Assertion.* import Regex.* object Email extends Newtype[String]: override inline def assertion = matches { start ~ anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~ literal("@") ~ anyRegexOf(alphanumeric, literal("-")).+ ~ literal(".").+ ~ anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~ end } type Email = Email.Type
  • 125. Smart Types in Scala 3! import zio.prelude.* import Assertion.* import Regex.* object Email extends Newtype[String]: override inline def assertion = matches { start ~ anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~ literal("@") ~ anyRegexOf(alphanumeric, literal("-")).+ ~ literal(".").+ ~ anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~ end } type Email = Email.Type
  • 126. Smart Types in Scala 3! import zio.prelude.* import Assertion.* import Regex.* object Email extends Newtype[String]: override inline def assertion = matches { start ~ anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~ literal("@") ~ anyRegexOf(alphanumeric, literal("-")).+ ~ literal(".").+ ~ anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~ end } type Email = Email.Type
  • 127. Smart Types in Scala 3! import zio.prelude.* import Assertion.* import Regex.* object Address extends Newtype[String]: override inline def assertion = !isEmptyString type Address = Address.Type
  • 128. Smart Types in Scala 3! import zio.prelude.* import Assertion.* import Regex.* object Address extends Newtype[String]: override inline def assertion = !isEmptyString type Address = Address.Type
  • 129. Smart Types in Scala 3! import zio.prelude.* import Assertion.* import Regex.* object Address extends Newtype[String]: override inline def assertion = !isEmptyString type Address = Address.Type
  • 130.
  • 131. Compile-time validations 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)
  • 132. Compile-time validations 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)
  • 133. Compile-time validations 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)
  • 134. Compile-time validations 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)
  • 135. Compile-time validations 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)
  • 136. Compile-time validations val name2: Name = Name("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0)) val email2: Email = Email("whatever") // COMPILATION ERROR! did not satisfy matches(^([w-.])*@([w-])*(.)*([w-]){2,4}$) val address2: Address = Address("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0)) val person2: Person = Person(name2, email2, address2)
  • 137. Compile-time validations val name2: Name = Name("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0)) val email2: Email = Email("whatever") // COMPILATION ERROR! did not satisfy matches(^([w-.])*@([w-])*(.)*([w-]){2,4}$) val address2: Address = Address("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0)) val person2: Person = Person(name2, email2, address2)
  • 138. Compile-time validations val name2: Name = Name("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0)) val email2: Email = Email("whatever") // COMPILATION ERROR! did not satisfy matches(^([w-.])*@([w-])*(.)*([w-]){2,4}$) val address2: Address = Address("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0)) val person2: Person = Person(name2, email2, address2)
  • 139. Compile-time validations val name2: Name = Name("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0)) val email2: Email = Email("whatever") // COMPILATION ERROR! did not satisfy matches(^([w-.])*@([w-])*(.)*([w-]){2,4}$) val address2: Address = Address("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0)) val person2: Person = Person(name2, email2, address2)
  • 140. Compile-time validations val name2: Name = Name("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0)) val email2: Email = Email("whatever") // COMPILATION ERROR! did not satisfy matches(^([w-.])*@([w-])*(.)*([w-]){2,4}$) val address2: Address = Address("") // COMPILATION ERROR! did not satisfy hasLength(notEqualTo(0)) val person2: Person = Person(name2, email2, address2)
  • 141. Compile-time validations val unknownName: Name = Name(scala.io.StdIn.readLine()) // COMPILATION ERROR! Could not validate Assertion at compile-time
  • 142. Compile-time validations val unknownName: Name = Name(scala.io.StdIn.readLine()) // COMPILATION ERROR! Could not validate Assertion at compile-time
  • 143. Compile-time validations val unknownName: Name = Name(scala.io.StdIn.readLine()) // COMPILATION ERROR! Could not validate Assertion at compile-time
  • 144. Runtime validations val name3: Validation[String, Name] = Name.make(scala.io.StdIn.readLine()) val email3: Validation[String, Email] = Email.make(scala.io.StdIn.readLine()) val address3: Validation[String, Address] = Address.make(scala.io.StdIn.readLine()) // Short-circuiting val person3a: Validation[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address) // Non short-circuiting val person3b: Validation[String, Person] = Validation.validateWith(name3, email3, address3)(Person.apply)
  • 145. Runtime validations val name3: Validation[String, Name] = Name.make(scala.io.StdIn.readLine()) val email3: Validation[String, Email] = Email.make(scala.io.StdIn.readLine()) val address3: Validation[String, Address] = Address.make(scala.io.StdIn.readLine()) // Short-circuiting val person3a: Validation[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address) // Non short-circuiting val person3b: Validation[String, Person] = Validation.validateWith(name3, email3, address3)(Person.apply)
  • 146. Runtime validations val name3: Validation[String, Name] = Name.make(scala.io.StdIn.readLine()) val email3: Validation[String, Email] = Email.make(scala.io.StdIn.readLine()) val address3: Validation[String, Address] = Address.make(scala.io.StdIn.readLine()) // Short-circuiting val person3a: Validation[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address) // Non short-circuiting val person3b: Validation[String, Person] = Validation.validateWith(name3, email3, address3)(Person.apply)
  • 147. Runtime validations val name3: Validation[String, Name] = Name.make(scala.io.StdIn.readLine()) val email3: Validation[String, Email] = Email.make(scala.io.StdIn.readLine()) val address3: Validation[String, Address] = Address.make(scala.io.StdIn.readLine()) // Short-circuiting val person3a: Validation[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address) // Non short-circuiting val person3b: Validation[String, Person] = Validation.validateWith(name3, email3, address3)(Person.apply)
  • 148. Runtime validations val name3: Validation[String, Name] = Name.make(scala.io.StdIn.readLine()) val email3: Validation[String, Email] = Email.make(scala.io.StdIn.readLine()) val address3: Validation[String, Address] = Address.make(scala.io.StdIn.readLine()) // Short-circuiting val person3a: Validation[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address) // Non short-circuiting val person3b: Validation[String, Person] = Validation.validateWith(name3, email3, address3)(Person.apply)
  • 149. Runtime validations val name3: Validation[String, Name] = Name.make(scala.io.StdIn.readLine()) val email3: Validation[String, Email] = Email.make(scala.io.StdIn.readLine()) val address3: Validation[String, Address] = Address.make(scala.io.StdIn.readLine()) // Short-circuiting val person3a: Validation[String, Person] = for { name <- name3 email <- email3 address <- address3 } yield Person(name, email, address) // Non short-circuiting val person3b: Validation[String, Person] = Validation.validateWith(name3, email3, address3)(Person.apply)
  • 150. Using Newtypes // We can define methods that work with Email def getUser(email: Email): String = Email.unwrap(email).split("@").head val myEmail = Email("jorge.vasquez@scalac.io") getUser(myEmail) // jorge.vasquez
  • 151. Using Newtypes // We can define methods that work with Email def getUser(email: Email): String = Email.unwrap(email).split("@").head val myEmail = Email("jorge.vasquez@scalac.io") getUser(myEmail) // jorge.vasquez
  • 152. Using Newtypes // We can define methods that work with Email def getUser(email: Email): String = Email.unwrap(email).split("@").head val myEmail = Email("jorge.vasquez@scalac.io") getUser(myEmail) // jorge.vasquez
  • 153. Using Newtypes // We can define methods that work with Email def getUser(email: Email): String = Email.unwrap(email).split("@").head val myEmail = Email("jorge.vasquez@scalac.io") getUser(myEmail) // jorge.vasquez
  • 154. Using Newtypes def capitalize(str: String): String = str.capitalize val myEmail = Email("jorge.vasquez@scalac.io") capitalize(myEmail)
  • 155. Using Newtypes def capitalize(str: String): String = str.capitalize val myEmail = Email("jorge.vasquez@scalac.io") capitalize(myEmail)
  • 156. Using Newtypes def capitalize(str: String): String = str.capitalize val myEmail = Email("jorge.vasquez@scalac.io") capitalize(myEmail)
  • 157. Using Newtypes def capitalize(str: String): String = str.capitalize val myEmail = Email("jorge.vasquez@scalac.io") capitalize(myEmail)
  • 158.
  • 159.
  • 160. Defining Subtypes in Scala 2! import zio.prelude._ import Assertion._ import Regex._ object Email extends Subtype[String] { override def assertion = assert { matches { start ~ anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~ literal("@") ~ anyRegexOf(alphanumeric, literal("-")).+ ~ literal(".").+ ~ anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~ end } } } type Email = Email.Type
  • 161. Defining Subtypes in Scala 2! import zio.prelude._ import Assertion._ import Regex._ object Email extends Subtype[String] { override def assertion = assert { matches { start ~ anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~ literal("@") ~ anyRegexOf(alphanumeric, literal("-")).+ ~ literal(".").+ ~ anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~ end } } } type Email = Email.Type
  • 162. Defining Subtypes in Scala 3! import zio.prelude.* import Assertion.* import Regex.* object Email extends Subtype[String]: override inline def assertion = matches { start ~ anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~ literal("@") ~ anyRegexOf(alphanumeric, literal("-")).+ ~ literal(".").+ ~ anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~ end } type Email = Email.Type
  • 163. Defining Subtypes in Scala 3! import zio.prelude.* import Assertion.* import Regex.* object Email extends Subtype[String]: override inline def assertion = matches { start ~ anyRegexOf(alphanumeric, literal("-"), literal(".")).+ ~ literal("@") ~ anyRegexOf(alphanumeric, literal("-")).+ ~ literal(".").+ ~ anyRegexOf(alphanumeric, literal("-")).between(2, 4) ~ end } type Email = Email.Type
  • 164. Using Subtypes def capitalize(str: String): String = str.capitalize val myEmail = Email("jorge.vasquez@scalac.io") capitalize(myEmail)
  • 165. Using Subtypes def capitalize(str: String): String = str.capitalize val myEmail = Email("jorge.vasquez@scalac.io") capitalize(myEmail)
  • 166. Using Subtypes def capitalize(str: String): String = str.capitalize val myEmail = Email("jorge.vasquez@scalac.io") capitalize(myEmail)
  • 167. Using Subtypes def capitalize(str: String): String = str.capitalize val myEmail = Email("jorge.vasquez@scalac.io") capitalize(myEmail)
  • 168.
  • 171. Summary • We need more precise data modelling for our applications
  • 172. Summary • We need more precise data modelling for our applications • There are several options, each one with its own limitations
  • 173. Summary • We need more precise data modelling for our applications • There are several options, each one with its own limitations • ZIO Prelude Smart Types provides a solution to those limitations
  • 174. Summary Smart Const. Opaque types + Smart Const. Newtype Refined + Smart Const. ZIO Prelude Smart Types Supported in Scala 2 √ √ √ √ Supported in Scala 3 √ √ √ Bye compile-time overhead! √ √ Bye runtime overhead! √ √ √
  • 176. Special thanks • Ziverge for organizing this conference
  • 177. Special thanks • Ziverge for organizing this conference • John De Goes, Kit Langton and Adam Fraser for guidance and support