SlideShare a Scribd company logo
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

Java 8 Workshop
Java 8 WorkshopJava 8 Workshop
Java 8 Workshop
Mario Fusco
 
Purely Functional Data Structures in Scala
Purely Functional Data Structures in ScalaPurely Functional Data Structures in Scala
Purely Functional Data Structures in Scala
Vladimir Kostyukov
 
Collections - Lists, Sets
Collections - Lists, Sets Collections - Lists, Sets
Collections - Lists, Sets
Hitesh-Java
 
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Philip Schwarz
 
Lazy java
Lazy javaLazy java
Lazy java
Mario Fusco
 
Java collections notes
Java collections notesJava collections notes
Java collections notes
Surendar Meesala
 
non-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parametersnon-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parameters
Philip Schwarz
 
Sequence and Traverse - Part 2
Sequence and Traverse - Part 2Sequence and Traverse - Part 2
Sequence and Traverse - Part 2
Philip Schwarz
 
Error Management: Future vs ZIO
Error Management: Future vs ZIOError Management: Future vs ZIO
Error Management: Future vs ZIO
John De Goes
 
1. Arrow Functions | JavaScript | ES6
1. Arrow Functions | JavaScript | ES61. Arrow Functions | JavaScript | ES6
1. Arrow Functions | JavaScript | ES6
pcnmtutorials
 
Railway Oriented Programming
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented Programming
Scott Wlaschin
 
The Functional Programmer's Toolkit (NDC London 2019)
The Functional Programmer's Toolkit (NDC London 2019)The Functional Programmer's Toolkit (NDC London 2019)
The Functional Programmer's Toolkit (NDC London 2019)
Scott Wlaschin
 
Domain Modeling with FP (DDD Europe 2020)
Domain Modeling with FP (DDD Europe 2020)Domain Modeling with FP (DDD Europe 2020)
Domain Modeling with FP (DDD Europe 2020)
Scott Wlaschin
 
Java Basics
Java BasicsJava Basics
Java Basics
Sunil OS
 
Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8
RichardWarburton
 
Domain Modeling in a Functional World
Domain Modeling in a Functional WorldDomain Modeling in a Functional World
Domain Modeling in a Functional World
Debasish Ghosh
 
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Philip Schwarz
 
L9 wrapper classes
L9 wrapper classesL9 wrapper classes
L9 wrapper classes
teach4uin
 

What's hot (20)

Java 8 Workshop
Java 8 WorkshopJava 8 Workshop
Java 8 Workshop
 
Purely Functional Data Structures in Scala
Purely Functional Data Structures in ScalaPurely Functional Data Structures in Scala
Purely Functional Data Structures in Scala
 
Optional in Java 8
Optional in Java 8Optional in Java 8
Optional in Java 8
 
Collections - Lists, Sets
Collections - Lists, Sets Collections - Lists, Sets
Collections - Lists, Sets
 
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
 
Lazy java
Lazy javaLazy java
Lazy java
 
Java collections notes
Java collections notesJava collections notes
Java collections notes
 
non-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parametersnon-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parameters
 
Sequence and Traverse - Part 2
Sequence and Traverse - Part 2Sequence and Traverse - Part 2
Sequence and Traverse - Part 2
 
Error Management: Future vs ZIO
Error Management: Future vs ZIOError Management: Future vs ZIO
Error Management: Future vs ZIO
 
1. Arrow Functions | JavaScript | ES6
1. Arrow Functions | JavaScript | ES61. Arrow Functions | JavaScript | ES6
1. Arrow Functions | JavaScript | ES6
 
Railway Oriented Programming
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented Programming
 
Java Strings
Java StringsJava Strings
Java Strings
 
The Functional Programmer's Toolkit (NDC London 2019)
The Functional Programmer's Toolkit (NDC London 2019)The Functional Programmer's Toolkit (NDC London 2019)
The Functional Programmer's Toolkit (NDC London 2019)
 
Domain Modeling with FP (DDD Europe 2020)
Domain Modeling with FP (DDD Europe 2020)Domain Modeling with FP (DDD Europe 2020)
Domain Modeling with FP (DDD Europe 2020)
 
Java Basics
Java BasicsJava Basics
Java Basics
 
Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8
 
Domain Modeling in a Functional World
Domain Modeling in a Functional WorldDomain Modeling in a Functional World
Domain Modeling in a Functional World
 
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
 
L9 wrapper classes
L9 wrapper classesL9 wrapper classes
L9 wrapper classes
 

Similar to Be Smart, Constrain Your Types to Free Your Brain!

ZIO Prelude - ZIO World 2021
ZIO Prelude - ZIO World 2021ZIO Prelude - ZIO World 2021
ZIO Prelude - ZIO World 2021
Jorge 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 2
Kirill 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
 
Scala - en bedre Java?
Scala - en bedre Java?Scala - en bedre Java?
Scala - en bedre Java?
Jesper Kamstrup Linnet
 
Benefits of Kotlin
Benefits of KotlinBenefits of Kotlin
Benefits of Kotlin
Benjamin Waye
 
Introduction to Scala
Introduction to ScalaIntroduction to Scala
Introduction to Scala
Aleksandar Prokopec
 
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
 
Java string handling
Java string handlingJava string handling
Java string handling
GaneshKumarKanthiah
 
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
HongAnhNguyn285885
 
Beginner's Python Cheat Sheet.pdf
Beginner's Python Cheat Sheet.pdfBeginner's Python Cheat Sheet.pdf
Beginner's Python Cheat Sheet.pdf
AkhileshKumar436707
 
(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 World
Daniel 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
 
beginners_python_cheat_sheet_pcc_all (1).pdf
beginners_python_cheat_sheet_pcc_all (1).pdfbeginners_python_cheat_sheet_pcc_all (1).pdf
beginners_python_cheat_sheet_pcc_all (1).pdf
ElNew2
 
Beginner's Python Cheat Sheet
Beginner's Python Cheat SheetBeginner's Python Cheat Sheet
Beginner's Python Cheat Sheet
Verxus
 
2. Python Cheat Sheet.pdf
2. Python Cheat Sheet.pdf2. Python Cheat Sheet.pdf
2. Python Cheat Sheet.pdf
MeghanaDBengalur
 

Similar to Be Smart, Constrain Your Types to 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)
 
beginners_python_cheat_sheet_pcc_all (1).pdf
beginners_python_cheat_sheet_pcc_all (1).pdfbeginners_python_cheat_sheet_pcc_all (1).pdf
beginners_python_cheat_sheet_pcc_all (1).pdf
 
Beginner's Python Cheat Sheet
Beginner's Python Cheat SheetBeginner's Python Cheat Sheet
Beginner's Python Cheat Sheet
 
2. Python Cheat Sheet.pdf
2. Python Cheat Sheet.pdf2. Python Cheat Sheet.pdf
2. Python Cheat Sheet.pdf
 

Recently uploaded

May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
Adele Miller
 
Graphic Design Crash Course for beginners
Graphic Design Crash Course for beginnersGraphic Design Crash Course for beginners
Graphic Design Crash Course for beginners
e20449
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
Paco van Beckhoven
 
Cyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdfCyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdf
Cyanic lab
 
A Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of PassageA Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of Passage
Philip Schwarz
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
Ortus Solutions, Corp
 
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Globus
 
Corporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMSCorporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMS
Tendenci - The Open Source AMS (Association Management Software)
 
Into the Box 2024 - Keynote Day 2 Slides.pdf
Into the Box 2024 - Keynote Day 2 Slides.pdfInto the Box 2024 - Keynote Day 2 Slides.pdf
Into the Box 2024 - Keynote Day 2 Slides.pdf
Ortus Solutions, Corp
 
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdfDominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
AMB-Review
 
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Natan Silnitsky
 
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Mind IT Systems
 
Vitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume MontevideoVitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume Montevideo
Vitthal Shirke
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
Globus
 
Using IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New ZealandUsing IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New Zealand
IES VE
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
Tier1 app
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
vrstrong314
 
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Globus
 
How to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good PracticesHow to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good Practices
Globus
 
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, BetterWebinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
XfilesPro
 

Recently uploaded (20)

May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
 
Graphic Design Crash Course for beginners
Graphic Design Crash Course for beginnersGraphic Design Crash Course for beginners
Graphic Design Crash Course for beginners
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
 
Cyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdfCyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdf
 
A Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of PassageA Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of Passage
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
 
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
 
Corporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMSCorporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMS
 
Into the Box 2024 - Keynote Day 2 Slides.pdf
Into the Box 2024 - Keynote Day 2 Slides.pdfInto the Box 2024 - Keynote Day 2 Slides.pdf
Into the Box 2024 - Keynote Day 2 Slides.pdf
 
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdfDominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
 
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
 
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
 
Vitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume MontevideoVitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume Montevideo
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
 
Using IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New ZealandUsing IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New Zealand
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
 
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
 
How to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good PracticesHow to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good Practices
 
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, BetterWebinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
Webinar: Salesforce Document Management 2.0 - Smarter, Faster, Better
 

Be Smart, Constrain Your Types to 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