SlideShare una empresa de Scribd logo
1 de 178
Descargar para leer sin conexión
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

Más contenido relacionado

La actualidad más candente

Functor, Apply, Applicative And Monad
Functor, Apply, Applicative And MonadFunctor, Apply, Applicative And Monad
Functor, Apply, Applicative And MonadOliver Daff
 
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021Natan Silnitsky
 
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Mario Fusco
 
JavaScript & Dom Manipulation
JavaScript & Dom ManipulationJavaScript & Dom Manipulation
JavaScript & Dom ManipulationMohammed Arif
 
Boost your productivity with Scala tooling!
Boost your productivity  with Scala tooling!Boost your productivity  with Scala tooling!
Boost your productivity with Scala tooling!MeriamLachkar1
 
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
 
Domain Modeling in a Functional World
Domain Modeling in a Functional WorldDomain Modeling in a Functional World
Domain Modeling in a Functional WorldDebasish Ghosh
 
The Functional Programming Triad of Map, Filter and Fold
The Functional Programming Triad of Map, Filter and FoldThe Functional Programming Triad of Map, Filter and Fold
The Functional Programming Triad of Map, Filter and FoldPhilip Schwarz
 
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...Philip Schwarz
 
Towards Functional Programming through Hexagonal Architecture
Towards Functional Programming through Hexagonal ArchitectureTowards Functional Programming through Hexagonal Architecture
Towards Functional Programming through Hexagonal ArchitectureCodelyTV
 
Domain Modeling Made Functional (KanDDDinsky 2019)
Domain Modeling Made Functional (KanDDDinsky 2019)Domain Modeling Made Functional (KanDDDinsky 2019)
Domain Modeling Made Functional (KanDDDinsky 2019)Scott Wlaschin
 
Why The Free Monad isn't Free
Why The Free Monad isn't FreeWhy The Free Monad isn't Free
Why The Free Monad isn't FreeKelley Robinson
 
Fundamental JavaScript [UTC, March 2014]
Fundamental JavaScript [UTC, March 2014]Fundamental JavaScript [UTC, March 2014]
Fundamental JavaScript [UTC, March 2014]Aaron Gustafson
 
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
 
Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in ScalaHermann Hueck
 
The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...Philip Schwarz
 

La actualidad más candente (20)

Functor, Apply, Applicative And Monad
Functor, Apply, Applicative And MonadFunctor, Apply, Applicative And Monad
Functor, Apply, Applicative And Monad
 
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
 
Zio in real world
Zio in real worldZio in real world
Zio in real world
 
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...
 
JavaScript & Dom Manipulation
JavaScript & Dom ManipulationJavaScript & Dom Manipulation
JavaScript & Dom Manipulation
 
Boost your productivity with Scala tooling!
Boost your productivity  with Scala tooling!Boost your productivity  with Scala tooling!
Boost your productivity with Scala tooling!
 
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...
 
Domain Modeling in a Functional World
Domain Modeling in a Functional WorldDomain Modeling in a Functional World
Domain Modeling in a Functional World
 
The Functional Programming Triad of Map, Filter and Fold
The Functional Programming Triad of Map, Filter and FoldThe Functional Programming Triad of Map, Filter and Fold
The Functional Programming Triad of Map, Filter and Fold
 
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
 
Monadic Java
Monadic JavaMonadic Java
Monadic Java
 
Towards Functional Programming through Hexagonal Architecture
Towards Functional Programming through Hexagonal ArchitectureTowards Functional Programming through Hexagonal Architecture
Towards Functional Programming through Hexagonal Architecture
 
Domain Modeling Made Functional (KanDDDinsky 2019)
Domain Modeling Made Functional (KanDDDinsky 2019)Domain Modeling Made Functional (KanDDDinsky 2019)
Domain Modeling Made Functional (KanDDDinsky 2019)
 
Why The Free Monad isn't Free
Why The Free Monad isn't FreeWhy The Free Monad isn't Free
Why The Free Monad isn't Free
 
Fundamental JavaScript [UTC, March 2014]
Fundamental JavaScript [UTC, March 2014]Fundamental JavaScript [UTC, March 2014]
Fundamental JavaScript [UTC, March 2014]
 
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)
 
Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in Scala
 
Preparing for Scala 3
Preparing for Scala 3Preparing for Scala 3
Preparing for Scala 3
 
The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...
 
Lazy java
Lazy javaLazy java
Lazy java
 

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

Similar a 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)
 
2. Python Cheat Sheet.pdf
2. Python Cheat Sheet.pdf2. Python Cheat Sheet.pdf
2. Python Cheat Sheet.pdf
 
python cheat sheat, Data science, Machine learning
python cheat sheat, Data science, Machine learningpython cheat sheat, Data science, Machine learning
python cheat sheat, Data science, Machine learning
 
Beginner's Python Cheat Sheet
Beginner's Python Cheat SheetBeginner's Python Cheat Sheet
Beginner's Python Cheat Sheet
 

Último

Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplatePresentation.STUDIO
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfkalichargn70th171
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionOnePlan Solutions
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrandmasabamasaba
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park masabamasaba
 
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburgmasabamasaba
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...masabamasaba
 
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...masabamasaba
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...Health
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...harshavardhanraghave
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...masabamasaba
 
Generic or specific? Making sensible software design decisions
Generic or specific? Making sensible software design decisionsGeneric or specific? Making sensible software design decisions
Generic or specific? Making sensible software design decisionsBert Jan Schrijver
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfproinshot.com
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Hararemasabamasaba
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesVictorSzoltysek
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park masabamasaba
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension AidPhilip Schwarz
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnAmarnathKambale
 

Último (20)

Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
Generic or specific? Making sensible software design decisions
Generic or specific? Making sensible software design decisionsGeneric or specific? Making sensible software design decisions
Generic or specific? Making sensible software design decisions
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdf
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 

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