SlideShare uma empresa Scribd logo
1 de 127
IMPERATIVE
ZIO PROGRAMMING
By Alexander Ioffe
@deusaquilus
Is Functional Programming
Easy?
Is Functional Programming
(With Effect Systems)
Easy?
IMPERATIVE
SEQUENCING
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
def read(file: File): String
def write(file: File, content: String): Unit
IMPERATIVE FUNCTIONAL
SEQUENCING
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
def read(file: File): String
def write(file: File, content: String): Unit
def read(file: File):
ZIO[Any, Throwable, String]
def write(file: File, content: String):
ZIO[Any, Throwable, Unit]
IMPERATIVE FUNCTIONAL
SEQUENCING
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
def read(file: File): String
def write(file: File, content: String): Unit
def read(file: File):
ZIO[Any, Throwable, String]
def write(file: File, content: String):
ZIO[Any, Throwable, Unit]
read(fileA).flatMap { textA =>
read(fileB).flatMap { textB =>
write(fileC, textA + textB)
}
}
IMPERATIVE FUNCTIONAL
SEQUENCING
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
def read(file: File): String
def write(file: File, content: String): Unit
def read(file: File):
ZIO[Any, Throwable, String]
def write(file: File, content: String):
ZIO[Any, Throwable, Unit]
for {
textA <- read(fileA)
textB <- read(fileB)
_ <- write(fileC, textA + textB)
} yield ()
FOR COMPREHENSIONS TO THE RESCUE
IMPERATIVE FUNCTIONAL
SEQUENCING
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
def read(file: File): String
def write(file: File, content: String): Unit
def read(file: File):
ZIO[Any, Throwable, String]
def write(file: File, content: String):
ZIO[Any, Throwable, Unit]
for {
textA <- read(fileA)
textB <- read(fileB)
_ <- write(fileC, textA + textB)
} yield ()
FOR COMPREHENSIONS TO THE RESCUE
For-Comprehension
All the things!
IMPERATIVE
BRANCHING
val rows: List[Row] = ...
val db = Database.open()
if (db.transactionsEnabled() && db.lazyFetchEnabled()) {
db.bulkInsert(rows)
}
class Database {
def transactionsEnabled(): Boolean
def lazyFetchEnabled(): Boolean
def bulkInsert(row: List[Row]): Unit
}
IMPERATIVE FUNCTIONAL
BRANCHING FOR COMPREHENSIONS TO THE RESCUE?
class Database {
def transactionsEnabled(): ZIO[Any, Throwable, Boolean]
def lazyFetchEnabled(): ZIO[Any, Throwable, Boolean]
def bulkInsert(row: List[Row]): ZIO[Any, Throwable, Unit]
}
val rows: List[Row] = ...
val db = Database.open()
if (db.transactionsEnabled() && db.lazyFetchEnabled()) {
db.bulkInsert(rows)
}
val rows: List[Row] = ...
for {
db <- Database.open
te <- db.transactionsEnabled()
lf <- db.lazyFetchEnabled()
_ <- {
if (te && lf)
db.bulkInsert(rows)
else
ZIO.unit
}
} yield ()
Why am I fetching
properties I might
never use???
class Database {
def transactionsEnabled(): Boolean
def lazyFetchEnabled(): Boolean
def bulkInsert(row: List[Row]): Unit
}
IMPERATIVE FUNCTIONAL
BRANCHING FOR COMPREHENSIONS TO THE RESCUE?
class Database {
def transactionsEnabled(): ZIO[Any, Throwable, Boolean]
def lazyFetchEnabled(): ZIO[Any, Throwable, Boolean]
def bulkInsert(row: List[Row]): ZIO[Any, Throwable, Unit]
}
val rows: List[Row] = ...
val db = Database.open()
if (db.transactionsEnabled() && db.lazyFetchEnabled()) {
db.bulkInsert(rows)
}
class Database {
def transactionsEnabled(): Boolean
def lazyFetchEnabled(): Boolean
def bulkInsert(row: List[Row]): Unit
}
val rows: List[Row] = ...
Database.open().flatMap { db =>
db.transactionsEnabled().flatMap { te =>
if (te)
db.lazyFetchEnabled().flatMap { lf =>
if (lf)
db.bulkInsert(rows)
else
ZIO.unit
}
else
ZIO.unit
}
}
Back to This???
IMPERATIVE FUNCTIONAL
LOOPING
class File {
def readLine(): String
def close(): Unit
}
var line: String = file.readLine()
while (line != null) {
buffer.append(line)
line = file.readLine()
}
IMPERATIVE FUNCTIONAL
THE PROGRAM... OR YOUR MIND?
class File {
def readLine(): String
def close(): Unit
}
var line: String = file.readLine()
while (line != null) {
buffer.append(line)
line = file.readLine()
}
class File {
def readLine(): ZIO[Any, Throwable, String]
def close(): ZIO[Any, Throwable, Unit]
}
file.readLine().flatMap { line0 =>
var line = line0
def whileFun(): ZIO[Any, Throwable, Unit] =
if (line != null)
buffer.append(line)
file.readLine().flatMap { lineN =>
line = lineN
whileFun()
}
else
ZIO.unit
whileFun()
Wait... there are actually cases
where WHILE is not reducible to
simple iteration examples???
LOOPING
IMPERATIVE FUNCTIONAL
var line: String = file.readLine()
def whileFun(): Unit =
if (line != null) {
buffer.append(line)
line = file.readLine()
whileFun()
} else {
()
}
class File {
def readLine(): String
def close(): Unit
}
class File {
def readLine(): ZIO[Any, Throwable, String]
def close(): ZIO[Any, Throwable, Unit]
}
file.readLine().flatMap { line0 =>
var line = line0
def whileFun(): ZIO[Any, Throwable, Unit] =
if (line != null)
buffer.append(line)
file.readLine().flatMap { lineN =>
line = lineN
whileFun()
}
else
ZIO.unit
whileFun()
THE PROGRAM... OR YOUR MIND?
LOOPING
IMPERATIVE FUNCTIONAL
LOOPING
def higherFunction(file: File) = {
var line: String = file.readLine()
def whileFun(): Unit =
if (line != null) {
buffer.append(line)
line = file.readLine()
whileFun()
} else {
()
}
}
P.S. NOT ACTUALLY USING GLOBALLY MUTABLE STATE!
class File {
def readLine(): String
def close(): Unit
}
class File {
def readLine(): ZIO[Any, Throwable, String]
def close(): ZIO[Any, Throwable, Unit]
}
def higherFunction(file: File) = {
file.readLine().flatMap { line0 =>
var line = line0
def whileFun(): ZIO[Any, Throwable, Unit] =
if (line != null)
buffer.append(line)
file.readLine().flatMap { lineN =>
line = lineN
whileFun()
}
else
ZIO.unit
whileFun()
}
IMPERATIVE
ERROR HANDLING EXPECT THE (MILDLY) UNEXPECTED
var file: JsonFile = JsonFile.from(path)
try {
file =
file.readToJson()
} catch {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
} finally {
file.close()
}
class JsonFile {
def readToJson(): Json
def close(): Unit
}
object JsonFile {
def from(path: String): JsonFile
}
IMPERATIVE FUNCTIONAL
ERROR HANDLING EXPECT THE (MILDLY) UNEXPECTED
var file: JsonFile = JsonFile.from(path)
try {
file =
file.readToJson()
} catch {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
} finally {
file.close()
}
succeed(JsonFile.from(path)).flatMap { jsonFile =>
jsonFile.readToJson()
}.catchSome {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
}.ensuring {
jsonFile.close().orDie
}
class JsonFile {
def readToJson(): ZIO[Any, Throwable, Json]
def close(): ZIO[Any, Throwable, Unit]
}
object JsonFile {
def from(path: String): JsonFile
}
class JsonFile {
def readToJson(): Json
def close(): Unit
}
object JsonFile {
def from(path: String): JsonFile
}
Once upon a time... IN JAVA 101
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
if (
db.transactionsEnabled() &&
db.lazyFetchEnabled()
)
db.bulkInsert(rows)
}
while (line != null) {
buffer.append(line)
line = file.readLine()
}
try {
file = JsonFile.open(path)
file.readToJson()
} catch {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
} finally {
file.close()
}
But these don't scale
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
if (
db.transactionsEnabled() &&
db.lazyFetchEnabled()
)
db.bulkInsert(rows)
}
while (line != null) {
buffer.append(line)
line = file.readLine()
}
val file = JsonFile.from(path)
try {
file.readToJson()
} catch {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
} finally {
file.close()
}
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
if (
db.transactionsEnabled() &&
db.lazyFetchEnabled()
)
db.bulkInsert(rows)
}
while (line != null) {
buffer.append(line)
line = file.readLine()
}
val file = JsonFile.from(path)
try {
file.readToJson()
} catch {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
} finally {
file.close()
}
But these don't scale
SO WE NEED THESE...
file.readLine().flatMap { line0 =>
var line = line0
def whileFun(): ZIO[Any, Throwable, Unit] =
if (line != null)
buffer.append(line)
file.readLine().flatMap { lineN =>
line = lineN
whileFun()
}
else
ZIO.unit
whileFun()
db.transactionsEnabled().flatMap { te =>
if (te)
db.lazyFetchEnabled().flatMap { lf =>
if (lf)
db.bulkInsert(rows)
else
ZIO.unit
}
else
ZIO.unit
}
succeed(JsonFile.from(path)).flatMap { jsonFile =>
jsonFile.readToJson()
}.catchSome {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
}.ensuring {
jsonFile.close().orDie
}
for {
textA <- read(fileA)
textB <- read(fileB)
_ <- write(fileC, textA + textB)
} yield ()
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
if (
db.transactionsEnabled() &&
db.lazyFetchEnabled()
)
db.bulkInsert(rows)
}
while (line != null) {
buffer.append(line)
line = file.readLine()
}
try {
file = JsonFile.open(path)
file.readToJson()
} catch {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
} finally {
file.close()
}
But these don't scale
OR DO They?
file.readLine().flatMap { line0 =>
var line = line0
def whileFun(): ZIO[Any, Throwable, Unit] =
if (line != null)
buffer.append(line)
file.readLine().flatMap { lineN =>
line = lineN
whileFun()
}
else
ZIO.unit
whileFun()
db.transactionsEnabled().flatMap { te =>
if (te)
db.lazyFetchEnabled().flatMap { lf =>
if (lf)
db.bulkInsert(rows)
else
ZIO.unit
}
else
ZIO.unit
}
for {
textA <- read(fileA)
textB <- read(fileB)
_ <- write(fileC, textA + textB)
} yield ()
succeed(JsonFile.from(path)).flatMap { jsonFile =>
jsonFile.readToJson()
}.catchSome {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
}.ensuring {
jsonFile.close().orDie
}
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
if (
db.transactionsEnabled() &&
db.lazyFetchEnabled()
)
db.bulkInsert(rows)
}
while (line != null) {
buffer.append(line)
line = file.readLine()
}
try {
file = JsonFile.open(path)
file.readToJson()
} catch {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
} finally {
file.close()
}
file.readLine().flatMap { line0 =>
var line = line0
def whileFun(): ZIO[Any, Throwable, Unit] =
if (line != null)
buffer.append(line)
file.readLine().flatMap { lineN =>
line = lineN
whileFun()
}
else
ZIO.unit
whileFun()
db.transactionsEnabled().flatMap { te =>
if (te)
db.lazyFetchEnabled().flatMap { lf =>
if (lf)
db.bulkInsert(rows)
else
ZIO.unit
}
else
ZIO.unit
}
for {
textA <- read(fileA)
textB <- read(fileB)
_ <- write(fileC, textA + textB)
} yield ()
WHAT IF I OLD YOU THAT
MONADIC SYNTAX IS JUST
INCIDENTAL COMPLEXITY?
succeed(JsonFile.from(path)).flatMap { jsonFile =>
jsonFile.readToJson()
}.catchSome {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
}.ensuring {
jsonFile.close().orDie
}
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
if (
db.transactionsEnabled() &&
db.lazyFetchEnabled()
)
db.bulkInsert(rows)
}
while (line != null) {
buffer.append(line)
line = file.readLine()
}
try {
file = JsonFile.open(path)
file.readToJson()
} catch {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
} finally {
file.close()
}
file.readLine().flatMap { line0 =>
var line = line0
def whileFun(): ZIO[Any, Throwable, Unit] =
if (line != null)
buffer.append(line)
file.readLine().flatMap { lineN =>
line = lineN
whileFun()
}
else
ZIO.unit
whileFun()
db.transactionsEnabled().flatMap { te =>
if (te)
db.lazyFetchEnabled().flatMap { lf =>
if (lf)
db.bulkInsert(rows)
else
ZIO.unit
}
else
ZIO.unit
}
for {
textA <- read(fileA)
textB <- read(fileB)
_ <- write(fileC, textA + textB)
} yield ()
succeed(JsonFile.from(path)).flatMap { jsonFile =>
jsonFile.readToJson()
}.catchSome {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
}.ensuring {
jsonFile.close().orDie
}
Imperative coding
is easy.
Write imperative style, translate
to Functional!
Functional code is
scaleable.
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
if (
db.transactionsEnabled() &&
db.lazyFetchEnabled()
)
db.bulkInsert(rows)
}
while (line != null) {
buffer.append(line)
line = file.readLine()
}
try {
file = JsonFile.open(path)
file.readToJson()
} catch {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
} finally {
file.close()
}
file.readLine().flatMap { line0 =>
var line = line0
def whileFun(): ZIO[Any, Throwable, Unit] =
if (line != null)
buffer.append(line)
file.readLine().flatMap { lineN =>
line = lineN
whileFun()
}
else
ZIO.unit
whileFun()
db.transactionsEnabled().flatMap { te =>
if (te)
db.lazyFetchEnabled().flatMap { lf =>
if (lf)
db.bulkInsert(rows)
else
ZIO.unit
}
else
ZIO.unit
}
for {
textA <- read(fileA)
textB <- read(fileB)
_ <- write(fileC, textA + textB)
} yield ()
Monadless
succeed(JsonFile.from(path)).flatMap { jsonFile =>
jsonFile.readToJson()
}.catchSome {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
}.ensuring {
jsonFile.close().orDie
}
RE-WRITING SEQUENCES FROM THIS... TO THIS!
FUNCTIONAL
IMPERATIVE
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
def read(file: File): String
def write(file: File, content: String): Unit
read(fileA).flatMap { textA =>
read(fileB).flatMap { textB =>
write(fileC, textA + textB)
}
}
def read(file: File):
ZIO[Any, Throwable, String]
def write(file: File, content: String):
ZIO[Any, Throwable, Unit]
FUNCTIONAL
IMPERATIVE
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
def read(file: File):
ZIO[Any, Throwable, String]
def write(file: File, content: String):
ZIO[Any, Throwable, Unit]
read(fileA).flatMap { textA =>
read(fileB).flatMap { textB =>
write(fileC, textA + textB)
}
}
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
RE-WRITING SEQUENCES MAKE TO LOOK LIKE IMPERATIVE
def read(file: File): String
def write(file: File, content: String): Unit
FUNCTIONAL
IMPERATIVE
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
def read(file: File): String
def write(file: File, content: String): Unit
def read(file: File):
ZIO[Any, Throwable, String]
def write(file: File, content: String):
ZIO[Any, Throwable, Unit]
read(fileA).flatMap { textA =>
read(fileB).flatMap { textB =>
write(fileC, textA + textB)
}
}
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB) :ZIO[..String]
:ZIO[..String]
:ZIO[..Unit]
RE-WRITING SEQUENCES ...BUT TYPE LIKE FUNCTINOAL
RE-WRITING SEQUENCES
FUNCTIONAL
IMPERATIVE
val textA = run(read(fileA))
val textB = run(read(fileB))
run(write(fileC, textA + textB))
def read(file: File): String
def write(file: File, content: String): Unit
def read(file: File):
ZIO[Any, Throwable, String]
def write(file: File, content: String):
ZIO[Any, Throwable, Unit]
PRESTO-CHANGE-OH
read(fileA).flatMap { textA =>
read(fileB).flatMap { textB =>
write(fileC, textA + textB)
}
}
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB) :String
:String
:Unit
RE-WRITING SEQUENCES
FUNCTIONAL
IMPERATIVE
val textA = run(read(fileA))
val textB = run(read(fileB))
run(write(fileC, textA + textB))
def read(file: File): String
def write(file: File, content: String): Unit
def read(file: File):
ZIO[Any, Throwable, String]
def write(file: File, content: String):
ZIO[Any, Throwable, Unit]
read(fileA).flatMap { textA =>
read(fileB).flatMap { textB =>
write(fileC, textA + textB)
}
}
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
THE RABIT IN THE HAT
:String
:String
:Unit
FUNCTIONAL
IMPERATIVE
defer {
val textA = run(read(fileA))
val textB = run(read(fileB))
run(write(fileC, textA + textB))
}
def read(file: File): String
def write(file: File, content: String): Unit
def read(file: File):
ZIO[Any, Throwable, String]
def write(file: File, content: String):
ZIO[Any, Throwable, Unit]
read(fileA).flatMap { textA =>
read(fileB).flatMap { textB =>
write(fileC, textA + textB)
}
}
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
RE-WRITING SEQUENCES
RE-WRITING SEQUENCES RUN MACRO, WRAP IN A BOW
RE-WRITING SEQUENCES
RE-WRITING SEQUENCES
FUNCTIONAL
IMPERATIVE
defer {
val textA = run(read(fileA))
val textB = run(read(fileB))
run(write(fileC, textA + textB))
}
def read(file: File): String
def write(file: File, content: String): Unit
def read(file: File):
ZIO[Any, Throwable, String]
def write(file: File, content: String):
ZIO[Any, Throwable, Unit]
read(fileA).flatMap { textA =>
read(fileB).flatMap { textB =>
write(fileC, textA + textB)
}
}
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
:ZIO[Any, Throwable, Unit]
RUN MACRO, WRAP IN A BOW
FUNCTIONAL
IMPERATIVE
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
def read(file: File): String
def write(file: File, content: String): Unit
def read(file: File):
ZIO[Any, Throwable, String]
def write(file: File, content: String):
ZIO[Any, Throwable, Unit]
read(fileA).flatMap { textA =>
read(fileB).flatMap { textB =>
write(fileC, textA + textB)
}
}
defer {
val textA = run(read(fileA))
val textB = run(read(fileB))
run(write(fileC, textA + textB))
}
RE-WRITING SEQUENCES
FEEL IMPERATIVE ACT FUNCTIONALLY
THAT'S ALL FOLKS
RE-WRITING SEQUENCES P.S. BETTER LIKE THIS
FUNCTIONAL
IMPERATIVE
defer {
val textA = read(fileA).run
val textB = read(fileB).run
write(fileC, textA + textB).run
}
def read(file: File): String
def write(file: File, content: String): Unit
def read(file: File):
ZIO[Any, Throwable, String]
def write(file: File, content: String):
ZIO[Any, Throwable, Unit]
read(fileA).flatMap { textA =>
read(fileB).flatMap { textB =>
write(fileC, textA + textB)
}
}
val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)
FEEL IMPERATIVE ACT FUNCTIONALLY
RE-WRITING BRANCHING START WITH IMPERATIVE STYLE
FUNCTIONAL
IMPERATIVE
class Database {
def transactionsEnabled(): ZIO[Any, Throwable, Boolean]
def lazyFetchEnabled(): ZIO[Any, Throwable, Boolean]
def bulkInsert(row: List[Row]): ZIO[Any, Throwable, Unit]
}
val db = Database.open()
if (
db.transactionsEnabled() &&
db.lazyFetchEnabled()
) {
db.bulkInsert(rows)
}
class Database {
def transactionsEnabled(): Boolean
def lazyFetchEnabled(): Boolean
def bulkInsert(row: List[Row]): Unit
}
Database.open().flatMap { db =>
db.transactionsEnabled().flatMap { te =>
if (te)
db.lazyFetchEnabled().flatMap { lf =>
if (lf)
db.bulkInsert(rows)
else
ZIO.unit
}
else
ZIO.unit
}
}
val db = Database.open()
if (
db.transactionsEnabled() &&
db.lazyFetchEnabled()
) {
db.bulkInsert(rows)
}
FUNCTIONAL
IMPERATIVE
class Database {
def transactionsEnabled(): ZIO[Any, Throwable, Boolean]
def lazyFetchEnabled(): ZIO[Any, Throwable, Boolean]
def bulkInsert(row: List[Row]): ZIO[Any, Throwable, Unit]
}
defer {
val db = Database.open().run
if (
db.transactionsEnabled().run &&
db.lazyFetchEnabled().run
) {
db.bulkInsert(rows).run
}
}
val db = Database.open()
if (
db.transactionsEnabled() &&
db.lazyFetchEnabled()
) {
db.bulkInsert(rows)
}
class Database {
def transactionsEnabled(): Boolean
def lazyFetchEnabled(): Boolean
def bulkInsert(row: List[Row]): Unit
}
Database.open().flatMap { db =>
db.transactionsEnabled().flatMap { te =>
if (te)
db.lazyFetchEnabled().flatMap { lf =>
if (lf)
db.bulkInsert(rows)
else
ZIO.unit
}
else
ZIO.unit
}
}
FEEL IMPERATIVE ACT FUNCTIONALLY
RE-WRITING BRANCHING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN!
FUNCTIONAL
IMPERATIVE
class Database {
def transactionsEnabled(): ZIO[Any, Throwable, Boolean]
def lazyFetchEnabled(): ZIO[Any, Throwable, Boolean]
def bulkInsert(row: List[Row]): ZIO[Any, Throwable, Unit]
}
defer {
val db = Database.open().run
if (
db.transactionsEnabled().run &&
db.lazyFetchEnabled().run
) {
db.bulkInsert(rows).run
}
}
val db = Database.open()
if (
db.transactionsEnabled() &&
db.lazyFetchEnabled()
) {
db.bulkInsert(rows)
}
class Database {
def transactionsEnabled(): Boolean
def lazyFetchEnabled(): Boolean
def bulkInsert(row: List[Row]): Unit
}
Database.open().flatMap { db =>
db.transactionsEnabled().flatMap { te =>
if (te)
db.lazyFetchEnabled().flatMap { lf =>
if (lf)
db.bulkInsert(rows)
else
ZIO.unit
}
else
ZIO.unit
}
}
FEEL IMPERATIVE ACT FUNCTIONALLY
RE-WRITING BRANCHING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN!
FUNCTIONAL
IMPERATIVE
FEEL IMPERATIVE ACT FUNCTIONALLY
RE-WRITING LOOPING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN!
class File {
def readLine(): String
def close(): Unit
}
var line: String = file.readLine()
while (line != null) {
buffer.append(line)
line = file.readLine()
}
class File {
def readLine(): ZIO[Any, Throwable, String]
def close(): ZIO[Any, Throwable, Unit]
}
file.readLine().flatMap { line0 =>
var line = line0
def whileFun(): ZIO[Any, Throwable, Unit] =
if (line != null)
buffer.append(line)
file.readLine().flatMap { lineN =>
line = lineN
whileFun()
}
else
ZIO.unit
whileFun()
FUNCTIONAL
IMPERATIVE
FEEL IMPERATIVE ACT FUNCTIONALLY
RE-WRITING LOOPING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN!
class File {
def readLine(): String
def close(): Unit
}
var line: String = file.readLine()
while (line != null) {
buffer.append(line)
line = file.readLine()
}
class File {
def readLine(): ZIO[Any, Throwable, String]
def close(): ZIO[Any, Throwable, Unit]
}
file.readLine().flatMap { line0 =>
var line = line0
def whileFun(): ZIO[Any, Throwable, Unit] =
if (line != null)
buffer.append(line)
file.readLine().flatMap { lineN =>
line = lineN
whileFun()
}
else
ZIO.unit
whileFun()
var line: String = file.readLine()
while (line != null) {
buffer.append(line)
line = file.readLine()
}
FUNCTIONAL
IMPERATIVE
defer {
var line: String = file.readLine().run
while (line != null) {
buffer.append(line)
line = file.readLine().run
}
}
FEEL IMPERATIVE ACT FUNCTIONALLY
RE-WRITING LOOPING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN!
class File {
def readLine(): String
def close(): Unit
}
var line: String = file.readLine()
while (line != null) {
buffer.append(line)
line = file.readLine()
}
class File {
def readLine(): ZIO[Any, Throwable, String]
def close(): ZIO[Any, Throwable, Unit]
}
file.readLine().flatMap { line0 =>
var line = line0
def whileFun(): ZIO[Any, Throwable, Unit] =
if (line != null)
buffer.append(line)
file.readLine().flatMap { lineN =>
line = lineN
whileFun()
}
else
ZIO.unit
whileFun()
FUNCTIONAL
IMPERATIVE
defer {
val line0 = file.readLine().run
var line: String = line0
while (line != null) {
buffer.append(line)
val lineN = file.readLine().run
line = lineN
}
}
FEEL IMPERATIVE ACT FUNCTIONALLY
RE-WRITING LOOPING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN!
class File {
def readLine(): String
def close(): Unit
}
var line: String = file.readLine()
while (line != null) {
buffer.append(line)
line = file.readLine()
}
class File {
def readLine(): ZIO[Any, Throwable, String]
def close(): ZIO[Any, Throwable, Unit]
}
file.readLine().flatMap { line0 =>
var line = line0
def whileFun(): ZIO[Any, Throwable, Unit] =
if (line != null)
buffer.append(line)
file.readLine().flatMap { lineN =>
line = lineN
whileFun()
}
else
ZIO.unit
whileFun()
FUNCTIONAL
IMPERATIVE
FEEL IMPERATIVE ACT FUNCTIONALLY
defer {
val line0 = file.readLine().run
var line: String = line0
while (line != null) {
buffer.append(line)
val lineN = file.readLine().run
line = lineN
}
}
RE-WRITING LOOPING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN!
class File {
def readLine(): String
def close(): Unit
}
var line: String = file.readLine()
while (line != null) {
buffer.append(line)
line = file.readLine()
}
class File {
def readLine(): ZIO[Any, Throwable, String]
def close(): ZIO[Any, Throwable, Unit]
}
file.readLine().flatMap { line0 =>
var line = line0
def whileFun(): ZIO[Any, Throwable, Unit] =
if (line != null)
buffer.append(line)
file.readLine().flatMap { lineN =>
line = lineN
whileFun()
}
else
ZIO.unit
whileFun()
FUNCTIONAL
IMPERATIVE
FEEL IMPERATIVE ACT FUNCTIONALLY
defer {
val line0 = file.readLine().run
var line: String = line0
while (line != null) {
buffer.append(line)
val lineN = file.readLine().run
line = lineN
}
}
RE-WRITING LOOPING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN!
class File {
def readLine(): String
def close(): Unit
}
var line: String = file.readLine()
while (line != null) {
buffer.append(line)
line = file.readLine()
}
class File {
def readLine(): ZIO[Any, Throwable, String]
def close(): ZIO[Any, Throwable, Unit]
}
file.readLine().flatMap { line0 =>
var line = line0
def whileFun(): ZIO[Any, Throwable, Unit] =
if (line != null)
buffer.append(line)
file.readLine().flatMap { lineN =>
line = lineN
whileFun()
}
else
ZIO.unit
whileFun()
RE-WRITING ERROR HANDLING EXPECT THE (MILDLY) UNEXPECTED
val file = JsonFile.from(path)
try {
file.readToJson()
} catch {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
} finally {
file.close()
}
class JsonFile {
def readToJson(): ZIO[Any, Throwable, Json]
def close(): ZIO[Any, Throwable, Unit]
}
object JsonFile {
def from(path: String): JsonFile
}
class JsonFile {
def readToJson(): Json
def close(): Unit
}
object JsonFile {
def open(path: String): JsonFile
}
FUNCTIONAL
IMPERATIVE
succeed(JsonFile.from(path))
.flatMap { file =>
file.readToJson().catchSome {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
}.ensuring {
file.close().orDie
}
FUNCTIONAL
IMPERATIVE
val file = JsonFile.from(path)
try {
file.readToJson()
} catch {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
} finally {
file.close()
}
class JsonFile {
def readToJson(): ZIO[Any, Throwable, Json]
def close(): ZIO[Any, Throwable, Unit]
}
object JsonFile {
def from(path: String): JsonFile
}
class JsonFile {
def readToJson(): Json
def close(): Unit
}
object JsonFile {
def open(path: String): JsonFile
}
val file: JsonFile = JsonFile.from(path)
try {
file.readToJson()
} catch {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
} finally {
file.close()
}
succeed(JsonFile.from(path))
.flatMap { file =>
file.readToJson().catchSome {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
}.ensuring {
file.close().orDie
}
RE-WRITING ERROR HANDLING EXPECT THE (MILDLY) UNEXPECTED
FUNCTIONAL
IMPERATIVE
defer {
val file = JsonFile.from(path)
try {
file.readToJson().run
} catch {
case e: IOException => handleIO(e).run
case e: DecodingException => handleDE(e).run
} finally {
file.close().run
}
}
val file = JsonFile.from(path)
try {
file.readToJson()
} catch {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
} finally {
file.close()
}
succeed(JsonFile.from(path))
.flatMap { file =>
file.readToJson().catchSome {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
}.ensuring {
file.close().orDie
}
class JsonFile {
def readToJson(): ZIO[Any, Throwable, Json]
def close(): ZIO[Any, Throwable, Unit]
}
object JsonFile {
def from(path: String): JsonFile
}
class JsonFile {
def readToJson(): Json
def close(): Unit
}
object JsonFile {
def open(path: String): JsonFile
} FEEL IMPERATIVE ACT FUNCTIONALLY
RE-WRITING ERROR HANDLING EXPECT THE (MILDLY) UNEXPECTED
FUNCTIONAL
IMPERATIVE
defer {
val file = JsonFile.from(path)
try {
file.readToJson().run
} catch {
case e: IOException => handleIO(e).run
case e: DecodingException => handleDE(e).run
} finally {
file.close().run
}
}
val file = JsonFile.from(path)
try {
file.readToJson()
} catch {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
} finally {
file.close()
}
succeed(JsonFile.from(path))
.flatMap { file =>
file.readToJson().catchSome {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
}.ensuring {
file.close().orDie
}
class JsonFile {
def readToJson(): ZIO[Any, Throwable, Json]
def close(): ZIO[Any, Throwable, Unit]
}
object JsonFile {
def from(path: String): JsonFile
}
class JsonFile {
def readToJson(): Json
def close(): Unit
}
object JsonFile {
def open(path: String): JsonFile
} FEEL IMPERATIVE ACT FUNCTIONALLY
RE-WRITING ERROR HANDLING EXPECT THE (MILDLY) UNEXPECTED
file.readLine().flatMap { line0 =>
var line = line0
def whileFun(): ZIO[Any, Throwable, Unit] =
if (line != null)
buffer.append(line)
file.readLine().flatMap { lineN =>
line = lineN
whileFun()
}
else
ZIO.unit
whileFun()
db.transactionsEnabled().flatMap { te =>
if (te)
db.lazyFetchEnabled().flatMap { lf =>
if (lf)
db.bulkInsert(rows)
else
ZIO.unit
}
else
ZIO.unit
}
Monadless
succeed(JsonFile.from(path)).flatMap { jsonFile =>
jsonFile.readToJson()
}.catchSome {
case e: IOException => handleIO(e)
case e: DecodingException => handleDE(e)
}.ensuring {
jsonFile.close().orDie
}
read(fileA).flatMap { textA =>
read(fileB).flatMap { textB =>
write(fileC, textA + textB)
}
}
defer {
val file = JsonFile.from(path)
try {
file.readToJson().run
} catch {
case e: IOException => handleIO(e).run
case e: DecodingException => handleDE(e).run
} finally {
file.close().run
}
}
defer {
val db = Database.open().run
if (
db.transactionsEnabled().run &&
db.lazyFetchEnabled().run
) {
db.bulkInsert(rows).run
}
}
defer {
val textA = read(fileA).run
val textB = read(fileB).run
write(fileC, textA + textB).run
}
defer {
val line0 = file.readLine().run
var line: String = line0
while (line != null) {
buffer.append(line)
val lineN = file.readLine().run
line = lineN
}
}
async {
val file = JsonFile.from(path)
try {
file.readToJson().await
} catch {
case e: IOException => handleIO(e).await
case e: DecodingException => handleDE(e).await
} finally {
file.close().await
}
}
async {
val db = Database.open().await
if (
db.transactionsEnabled().await &&
db.lazyFetchEnabled().await
) {
db.bulkInsert(rows).await
}
}
async {
val textA = read(fileA).await
val textB = read(fileB).await
write(fileC, textA + textB).await
}
async {
val line0 = file.readLine().await
var line: String = line0
while (line != null) {
buffer.append(line)
val lineN = file.readLine().await
line = lineN
}
}
ISN'T THIS ASYNC-AWAIT ?
defer {
val p = List(joe, jack, jill).run
val a = p.addresses.run
(p, a)
}
defer {
val p =
query[Person].run
val a =
query[Address].filter(a.fk == p.id).run
(p, a)
}
Works for Collections! Works for Queries!
async {
val file = JsonFile.from(path)
try {
file.readToJson().await
} catch {
case e: IOException => handleIO(e).await
case e: DecodingException => handleDE(e).await
} finally {
file.close().await
}
}
async {
val db = Database.open().await
if (
db.transactionsEnabled().await &&
db.lazyFetchEnabled().await
) {
db.bulkInsert(rows).await
}
}
async {
val textA = read(fileA).await
val textB = read(fileB).await
write(fileC, textA + textB).await
}
async {
val line0 = file.readLine().await
var line: String = line0
while (line != null) {
buffer.append(line)
val lineN = file.readLine().await
line = lineN
}
}
NO
defer {
val p = List(joe, jack, jill).run
val a = p.addresses.run
(p, a)
}
defer {
val p =
query[Person].run
val a =
query[Address].filter(a.fk == p.id).run
(p, a)
}
Works for Collections! Works for Queries!
Wouldn't Everyone want this?
defer {
val line0 = file.readLine().run
var line: String = line0
while (line != null) {
buffer.append(line)
val lineN = file.readLine().run
line = lineN
}
}
defer {
val textA = run(read(fileA))
val textB = run(read(fileB))
run(write(fileC, textA + textB))
}
defer {
val db = Database.open().run
if (
db.transactionsEnabled().run &&
db.lazyFetchEnabled().run
) {
db.bulkInsert(rows).run
}
}
defer {
val file = JsonFile.from(path)
try {
file.readToJson().run
} catch {
case e: IOException => handleIO(e).run
case e: DecodingException => handleDE(e).run
} finally {
file.close().run
}
}
SUPPORTS THESE
and...
defer {
val line0 = file.readLine().run
var line: String = line0
while (line != null) {
buffer.append(line)
val lineN = file.readLine().run
line = lineN
}
}
defer {
val textA = run(read(fileA))
val textB = run(read(fileB))
run(write(fileC, textA + textB))
}
defer {
val db = Database.open().run
if (
db.transactionsEnabled().run &&
db.lazyFetchEnabled().run
) {
db.bulkInsert(rows).run
}
}
defer {
val file = JsonFile.from(path)
try {
file.readToJson().run
} catch {
case e: IOException => handleIO(e).run
case e: DecodingException => handleDE(e).run
} finally {
file.close().run
}
}
getWebsites()
.flatMap { websites =>
ZIO.foreach(websites)(
postUpdate(_)
)
}
for (site <- getWebsites()) {
postUpdate(site)
}
FOR-LOOP
IMPERATIVE TRANSLATION
defer {
val line0 = file.readLine().run
var line: String = line0
while (line != null) {
buffer.append(line)
val lineN = file.readLine().run
line = lineN
}
}
defer {
val textA = run(read(fileA))
val textB = run(read(fileB))
run(write(fileC, textA + textB))
}
TRANSLATION
defer {
val db = Database.open().run
if (
db.transactionsEnabled().run &&
db.lazyFetchEnabled().run
) {
db.bulkInsert(rows).run
}
}
defer {
val file = JsonFile.from(path)
try {
file.readToJson().run
} catch {
case e: IOException => handleIO(e).run
case e: DecodingException => handleDE(e).run
} finally {
file.close().run
}
}
getWebsites()
.flatMap { websites =>
ZIO.foreach(websites)(
postUpdate(_)
)
}
IMPERATIVE
FOR
defer {
for (site <- getWebsites().run) {
postUpdate(site).run
}
}
defer {
val line0 = file.readLine().run
var line: String = line0
while (line != null) {
buffer.append(line)
val lineN = file.readLine().run
line = lineN
}
}
defer {
val textA = run(read(fileA))
val textB = run(read(fileB))
run(write(fileC, textA + textB))
}
TRANSLATION
defer {
val db = Database.open().run
if (
db.transactionsEnabled().run &&
db.lazyFetchEnabled().run
) {
db.bulkInsert(rows).run
}
}
defer {
val file = JsonFile.from(path)
try {
file.readToJson().run
} catch {
case e: IOException => handleIO(e).run
case e: DecodingException => handleDE(e).run
} finally {
file.close().run
}
}
getWebsites()
.flatMap { websites =>
ZIO.foreachPar(websites)(
postUpdate(_)
)
}
IMPERATIVE
FOR
defer(Params(Collect.Parallel)) {
for (site <- getWebsites().run) {
postUpdate(site).run
}
}
ZIO-Tailored
Features
Error
Channels
Transparency
of Rewrite
Logic
ZIO-DIRECT
Direct Programming tailored to ZIO
Correctness
Using
Refs
{x ⟺ y}
1 2 3
4 5
val out: ZIO[CustomerConfig & DistributorConfig, Throwable, (Customer, Distributor)] =
defer {
val custUrl: String = ZIO.service[CustomerConfig].run.url
val distUrl: String = ZIO.service[DistributorConfig].run.url
(parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run))
}
ZIO-DIRECT
Direct Programming tailored to ZIO
def httpGet(url: String): ZIO[Any, Throwable, String]
def parseCustomer(customer: String): Customer
def parseDistrubutor(customer: String): Distributor
case class CustomerConfig(url: String)
case class DistributorConfig(url: String)
val out: ZIO[CustomerConfig & DistributorConfig, Throwable, (Customer, Distributor)] =
defer {
val custUrl: String = ZIO.service[CustomerConfig].run.url
val distUrl: String = ZIO.service[DistributorConfig].run.url
(parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run))
}
ZIO-DIRECT
Environment? Composes!
API
def httpGet(url: String): ZIO[Any, Throwable, String]
def parseCustomer(customer: String): Customer
def parseDistrubutor(customer: String): Distributor
case class CustomerConfig(url: String)
case class DistributorConfig(url: String)
val out: ZIO[CustomerConfig & DistributorConfig, IOException, (Customer, Distributor)] =
defer {
val custUrl: String = ZIO.service[CustomerConfig].run.url
val distUrl: String = ZIO.service[DistributorConfig].run.url
(parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run))
}
def httpGet(url: String): ZIO[Any, IOException, String]
def parseCustomer(customer: String): Customer
def parseDistrubutor(customer: String): Distributor
case class CustomerConfig(url: String)
case class DistributorConfig(url: String)
ZIO-DIRECT
Errors? Compose!
API
val out: ZIO[..., CustomerGetException | DistributorGetException, (...)] =
defer {
val custUrl: String = ZIO.service[CustomerConfig].run.url
val distUrl: String = ZIO.service[DistributorConfig].run.url
(parseCustomer(httpGetCustomer(custUrl).run), parseDistrubutor(httpGetDistributor(distUrl).run))
}
def httpGetCustomer(url: String): ZIO[Any, CustomerGetException, String]
def httpGetDistributor(url: String): ZIO[Any, DistrubutorGetException, String]
def parseCustomer(customer: String): Customer
def parseDistrubutor(customer: String): Distributor
case class CustomerConfig(url: String)
case class DistributorConfig(url: String)
API
ZIO-DIRECT
Errors? Compose!
val out: ZIO[..., Exception, (...)] =
defer(Params(TypeUnion.LeastUpper)) {
val custUrl: String = ZIO.service[CustomerConfig].run.url
val distUrl: String = ZIO.service[DistributorConfig].run.url
(parseCustomer(httpGetCustomer(custUrl).run), parseDistrubutor(httpGetDistributor(distUrl).run))
}
def httpGetCustomer(url: String): ZIO[Any, CustomerGetException, String]
def httpGetDistributor(url: String): ZIO[Any, DistrubutorGetException, String]
def parseCustomer(customer: String): Customer
def parseDistrubutor(customer: String): Distributor
case class CustomerConfig(url: String)
case class DistributorConfig(url: String)
API
ZIO-DIRECT
Errors? Compose... in Two Ways!
ZIO-DIRECT
Want to see it up close?
ZIO-DIRECT
Use defer.info
root> compile
...
[info] -- Info: /Users/me/zio-direct/src/test/scala-3.x/zio/direct/examples/TailoredForZio.scala:40:8
[info] 40 | val out =
[info] | ^
[info] |Computed Type: ZIO[CustomerConfig & DistributorConfig, CustomerGetException | DistrubutorGetException, Tuple2[Customer, Distributor]]
ZIO-DIRECT
root> compile
...
[info] -- Info: /Users/me/zio-direct/src/test/scala-3.x/zio/direct/examples/TailoredForZio.scala:40:8
[info] 40 | val out =
[info] | ^
[info] |Computed Type: ZIO[CustomerConfig & DistributorConfig, CustomerGetException | DistrubutorGetException, Tuple2[Customer, Distributor]]
Use defer.info
ZIO-DIRECT
Use defer.info or defer.verbose
root> compile
[info] -- Info: /Users/me/zio-direct/src/test/scala-3.x/zio/direct/examples/TailoredForZio.scala:40:8
[info] 40 | val out =
[info] | ^
[info] |Computed Type: ZIO[CustomerConfig & DistributorConfig, CustomerGetException | DistrubutorGetException, Tuple2[Customer, Distributor]]
ZIO-DIRECT
Use defer.info or defer.verbose
root> compile
[info] -- Info: /Users/me/zio-direct/src/test/scala-3.x/zio/direct/examples/TailoredForZio.scala:40:8
[info] 40 | val out =
[info] | ^
[info] |Computed Type: ZIO[CustomerConfig & DistributorConfig, CustomerGetException | DistrubutorGetException, Tuple2[Customer, Distributor]]
ZIO.service[CustomerConfig].map { (sm: CustomerConfig) =>
sm.url
}.flatMap { custUrl =>
...
}
ZIO-DIRECT
Use defer.info or defer.verbose
root> compile
[info] -- Info: /Users/me/zio-direct/src/test/scala-3.x/zio/direct/examples/TailoredForZio.scala:40:8
[info] 40 | val out =
[info] | ^
[info] |Computed Type: ZIO[CustomerConfig & DistributorConfig, CustomerGetException | DistrubutorGetException, Tuple2[Customer, Distributor]]
ZIO.service[CustomerConfig].map { (sm: CustomerConfig) =>
val runVal: CustomerConfig = sm
sm.url
}.flatMap { (v: String) =>
val custUrl: String = v
}
ZIO-DIRECT
Use defer.info or defer.verbose
root> compile
=== Reconstituted Code ========
ZIO.service[CustomerConfig].map(((sm: CustomerConfig) => {
val runVal: CustomerConfig = sm
runVal.url
})).asInstanceOf[ZIO[_, _, String]].flatMap(((v: String) => {
val custUrl: String = v
ZIO.service[DistributorConfig].map(((`sm₂`: DistributorConfig) => {
val `runVal₂`: DistributorConfig = `sm₂`
`runVal₂`.url
})).asInstanceOf[ZIO[_, _, String]].flatMap(((`v₂`: String) => {
val distUrl: String = `v₂`
ZIO.collectAll(Chunk.from(List.apply(httpGetCustomer(custUrl), httpGetDistributor(distUrl)))).map(((terms: Chunk[Any]) => {
val iter: Iterator[Any] = terms.iterator
{
val `runVal₃`: String = iter.next.asInstanceOf[String]
val `runVal₄`: String = iter.next.asInstanceOf[String]
Tuple2.apply(parseCustomer(`runVal₃`), parseDistrubutor(`runVal₄`))
}.asInstanceOf[Tuple2[Customer, Distributor]]
})).asInstanceOf[ZIO[_, _, Tuple2[Customer, Distributor]]]
}))
}))
ZIO-DIRECT
Use defer.info or defer.verbose
val out =
defer {
val custUrl: String = ZIO.service[CustomerConfig].run.url
val distUrl: String = ZIO.service[DistributorConfig].run.url
(parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run))
}
ZIO-DIRECT
Wait... how do you do that?
That's an arbitrary
construct...
how do you do that?
val out =
defer {
val custUrl: String = ZIO.service[CustomerConfig].run.url
val distUrl: String = ZIO.service[DistributorConfig].run.url
(parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run))
}
ZIO-DIRECT
Mystery Challenge... spot the IOs!
ZIO.service[CustomerConfig].flatMap { cc =>
val custUrl: String = cc.url
ZIO.service[DistributorConfig].flatMap { dc =>
val distUrl: String = dc.url
ZIO.collectAll(Chunk(httpGet(custUrl), (httpGet(distUrl)))).map { col =>
val iter = col.iterator
(parseCustomer(iter.next()), parseDistrubutor(iter.next()))
}
}
}
val out =
defer {
val custUrl: String = ZIO.service[CustomerConfig].run.url
val distUrl: String = ZIO.service[DistributorConfig].run.url
(parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run))
}
ZIO-DIRECT
Pull them out of the line body...
ZIO.service[CustomerConfig].flatMap { cc =>
val custUrl: String = cc.url
ZIO.service[DistributorConfig].flatMap { dc =>
val distUrl: String = dc.url
ZIO.collectAll(Chunk(httpGet(custUrl), (httpGet(distUrl)))).map { col =>
val iter = col.iterator
(parseCustomer(iter.next()), parseDistrubutor(iter.next()))
}
}
}
val out =
defer {
val custUrl: String = ZIO.service[CustomerConfig].run.url
val distUrl: String = ZIO.service[DistributorConfig].run.url
(parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run))
}
ZIO-DIRECT
Collect, iterate, and splice back in!
ZIO.service[CustomerConfig].flatMap { cc =>
val custUrl: String = cc.url
ZIO.service[DistributorConfig].flatMap { dc =>
val distUrl: String = dc.url
ZIO.collectAll(Chunk(httpGet(custUrl), (httpGet(distUrl)))).map { col =>
val iter = col.iterator
(parseCustomer(iter.next()), parseDistrubutor(iter.next()))
}
}
}
ZIO-DIRECT
If you have no mutable data structures
and no lazy actions... things in here
cannot interact between each other...
therefore, I can extract them!
(parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run))
ZIO-DIRECT
Things in defer blocks must be:
EAGER + IMMUTABLE
(unless they are wrapped in an effect)
CORRECTENESS
{x ⟺ y}
yes... it's very important! CORRECTNESS
defer(Params(Verify.None)) {
val db = Database.open.run
while (db.hasNextRow()) {
if (db.lockNextRow())
doSomethingWith(db.nextRow().run)
else
wait()
}
}
class Database {
def nextRow(): ZIO[Any, Throwable, Row]
def hasNextRow(): Boolean
def lockNextRow(): Boolean //somehow not an effect
}
API
yes... it's very important! CORRECTNESS
defer(Params(Verify.None)) {
val db = Database.open.run
while (db.hasNextRow()) {
if (db.lockNextRow())
doSomethingWith(db.nextRow().run)
else
wait()
}
}
class Database {
def nextRow(): ZIO[Any, Throwable, Row]
def hasNextRow(): Boolean
def lockNextRow(): Boolean //somehow not an effect
}
API
Database.open.flatMap { db =>
def whileFun(): ZIO[Any, Throwable, Unit] =
if (db.hasNextRow())
db.nextRow().flatMap { row =>
// Too late to check if row is locked, we already READ IT!!
if (db.lockNextRow()) doSomethingWith(row) else waitT()
whileFun()
}
else
ZIO.unit
whileFun()
}
yes... it's very important! CORRECTNESS
defer(Params(Verify.None)) {
val db = Database.open.run
while (db.hasNextRow()) {
if (db.lockNextRow())
doSomethingWith(db.nextRow().run)
else
wait()
}
}
class Database {
def nextRow(): ZIO[Any, Throwable, Row]
def hasNextRow(): Boolean
def lockNextRow(): Boolean //somehow not an effect
}
API
=== Reconstituted Code ========
FunctionalObjectModel.Database.open.flatMap(((v: Database) => {
val db: Database = v
def whileFunc: ZIO[Any, Throwable, Unit] = if (db.hasNextRow) if (db.lockNextRow) db.nextRow.map(((sm: Row) => {
val runVal: Row = sm
FunctionalObjectModel.doSomethingWith(runVal)
})) else zio.ZIO.succeed(FunctionalObjectModel.waitT).flatMap(((`v₂`: Unit) => whileFunc)) else zio.ZIO.succeed(())
whileFunc
}))
yes... it's very important! CORRECTNESS
defer(Params(Verify.None)) {
val db = Database.open.run
while (ZIO.succeed(db.hasNextRow()).run) {
if (ZIO.succeed(db.lockNextRow()).run)
doSomethingWith(db.nextRow().run)
else
wait()
}
}
class Database {
def nextRow(): ZIO[Any, Throwable, Row]
def hasNextRow(): Boolean
def lockNextRow(): Boolean //somehow not an effect
}
API
Here's the thing you shouldn't do! CORRECTNESS
defer(Params(Verify.None)) {
val db = Database.open.run
(db.lockNextRow(), db.nextRow().run)
=== Reconstituted Code ========
FunctionalObjectModel.Database.open.flatMap(((v: Database) => {
val db: Database = v
db.nextRow.map(((sm: Row) => {
val runVal: Row = sm
Tuple2.apply(db.lockNextRow, runVal)
}))
}))
CORRECTNESS
val a = effectA.run
<expressions-using a>
val b = effectB.run
<expressions-using a, b>
val c = effectC.run
<expressions-using a, b, c>
effectA.flatMap { a =>
<expressions-using a>
effectB.flatMap { b =>
<expressions-using a, b>
effectC.flatMap {
<expressions-using a, b, c>
}
}
}
The Basic Intuition
CORRECTNESS
val a = effectA.run
<expressions-using a>
val b = effectB.run
<expressions-using a, b>
val c = effectC.run
<expressions-using a, b, c>
effectA.flatMap { a =>
<expressions-using a>
effectB.flatMap { b =>
<expressions-using a, b>
effectC.flatMap {
<expressions-using a, b, c>
}
}
}
val a = effectA.run
<expressions-using a>
val b = effectB.run
<expressions-using a, b>
<expressions-using effectC.run, effectD.run>
effectA.flatMap { a =>
<expressions-using a>
effectB.flatMap { b =>
<expressions-using a, b>
foreach(effectC, effectD) { list =>
<expressions-using list.get(0), list.get(1)>
}
}
}
The Basic Intuition
CORRECTNESS
val a = effectA.run
<expressions-using a>
val b = effectB.run
<expressions-using a, b>
val c = effectC.run
<expressions-using a, b, c>
val a = effectA.run
<expressions-using a>
val b = effectB.run
<expressions-using a, b>
<expressions-using effectC.run, effectD.run>
effectA.flatMap { a =>
<expressions-using a>
effectB.flatMap { b =>
<expressions-using a, b>
foreach(effectC, effectD) { list =>
<expressions-using list.get(0), list.get(1)>
}
}
}
The Basic Intuition
NO INTER-LINE INTERACTIONS
NO INTER-LINE INTERACTIONS
effectA.flatMap { a =>
<expressions-using a>
effectB.flatMap { b =>
<expressions-using a, b>
effectC.flatMap { c =>
<expressions-using a, b, c>
}
}
}
CORRECTNESS
val a = effectA.run
<expressions-using a>
val b = effectB.run
<expressions-using a, b>
val c = effectC.run
<expressions-using a, b, c>
effectA.flatMap { a =>
<expressions-using a>
effectB.flatMap { b =>
<expressions-using a, b>
effectC.flatMap { c =>
<expressions-using a, b, c>
}
}
}
val a = effectA.run
<expressions-using a>
val b = effectB.run
<expressions-using a, b>
<expressions-using effectC.run, effectD.run>
effectA.flatMap { a =>
<expressions-using a>
effectB.flatMap { b =>
<expressions-using a, b>
foreach(effectC, effectD) { list =>
<expressions-using list.get(0), list.get(1)>
}
}
}
The Basic Intuition
ALL INTERACTIONS HAPPEN DOWNWARD
ALL INTERACTIONS HAPPEN DOWNWARD
CORRECTNESS
effectA.flatMap { a =>
<expressions-using a>
effectB.flatMap { b =>
<expressions-using a, b>
effectC.flatMap { c =>
<expressions-using a, b, c>
}
}
}
effectA.flatMap { a =>
<expressions-using a>
effectB.flatMap { b =>
<expressions-using a, b>
ZIO.foreach(effectC, effectD) { list =>
<expressions-using list.get(0), list.get(1)>
}
}
}
The Basic Intuition
ALL INTERACTIONS HAPPEN DOWNWARD
ALL INTERACTIONS HAPPEN DOWNWARD
SEQUENTIALITY =:= NESTING
CORRECTNESS
effectA.flatMap { a =>
<expressions-using a>
effectB.flatMap { b =>
<expressions-using a, b>
effectC.flatMap { c =>
<expressions-using a, b, c>
}
}
}
effectA.flatMap { a =>
<expressions-using a>
effectB.flatMap { b =>
<expressions-using a, b>
foreach(effectC, effectD) { list =>
<expressions-using list.get(0), list.get(1)>
}
}
}
The Basic Intuition
ALL INTERACTIONS HAPPEN DOWNWARD
ALL INTERACTIONS HAPPEN DOWNWARD
for {
a <- effectA.run
_ = <expressions-using a>
b <- effectB.run
_ = <expressions-using a, b>
c <- effectC.run
} yield <expressions-using a, b, c>
CORRECTNESS
The Re-Write Rules
- If the statement already transformed e.g. defer { defer { ... } }:
- Chain it in a
fl
atMap and continue
- If statement is a try:
- Recurse on the body, then the catch clauses, then the
fi
nalizer if it exists.
- Chain all of them in nested
fl
atMaps (or maps if they have no run statements inside)
- If statement is a throw, or unsafe { ... } block:
- Chain it in a
fl
atMap (or map if it has run statements inside)
- If statement is an if, for-loop, or match statement
- Recurse on the pre
fi
x/condition and then the body statement(s).
- Chain both in
fl
atMaps (or maps if they have no run statements inside)
- If statement is a run call:
- Chain it in a
fl
atMap
- If statement is a block e.g. { a; b; c }
- Chain each clause in a
fl
atMap (or maps if it has no `run` statements inside)
- Otherwise if the statement has no vars, defs, classes mutable state, or any other forbidden construct:
- Extract each run call into a ZIO. collect
- Then splice in the iteration steps in each call-site.
- Chain the whole thing in a
fl
atMap
CORRECTNESS
Other Forbidden Things!
• lazy value declarations
• class declarations
• function declarations
• lambdas/closures
• implicit variables/functions
OTHER THINGS
FORBIDDEN IN
DEFER-CLAUSES
(Unless they are in the .run calls)
Constraints Certainty Capability
CORRECTNESS
NO lazy value declarations
NO class declarations
NO function declarations
NO lambdas/closures
NO implicit variables/functions
Cut-Points Work
Splicing Correct
Sequencing Correct
Generated Code Correct
Direct-Style Syntax
Language-Level Constructs
CORRECTNESS
Mutation... Forbidden!
var x = 1
defer.info {
({ x = x + 1; x }, ZIO.succeed(x).run)
}
> runMain
(2, 1) ...but why
not (2, 2)
???
CORRECTNESS
Mutable Collections... Forbidden!
> runMain
(2, 1)
defer.info {
({ buff.update(0, buff(0) + 1); buff(0) }, ZIO.succeed(buff(0)).run)
}
CORRECTNESS
Mutable Collections... Forbidden!
Unless inside run(...) clauses
Things in defer blocks must be:
EAGER + IMMUTABLE
(unless they are wrapped in an effect!!!
...and called with .run)
CORRECTENESS
ARGUMENT 1 It is not Referentially Transparent
ARGUMENT 1 It is not Referentially Transparent
Definition:
An expression shall called referentially transparent
if it can be replaced with its corresponding value
without changing the program's behavior.
Interpretation:
Ordering of value-terms should not matter.
ARGUMENT 1 It is not Referentially Transparent
defer {
val int = run(List(1, 2))
val bool = run(List(true, false))
(int, bool)
}
// List((1, true), (1, false), (2, true), (2, false))
defer {
val bool = run(List(true, false))
val int = run(List(1, 2))
(int, bool)
}
// List((1, true), (2, true), (1, false), (2, false))
ARGUMENT 1 It is not Referentially Transparent
defer.info {
val int = run(List(1, 2))
val bool = run(List(true, false))
(int, bool)
}
// List((1, true), (1, false), (2, true), (2, false))
defer.info {
val bool = run(List(true, false))
val int = run(List(1, 2))
(int, bool)
}
// List((1, true), (2, true), (1, false), (2, false))
List(1, 2).flatMap { int =>
List(true, false).map { bool =>
(int, bool)
}
}
List(true, false).flatMap { int =>
List(1, 2).map { bool =>
(int, bool)
}
}
ARGUMENT 1 It is not Referentially Transparent
List(1, 2).flatMap { int =>
List(true, false).map { bool =>
(int, bool)
}
}
List(true, false).flatMap { int =>
List(1, 2).map { bool =>
(int, bool)
}
}
... but the things on the left
hand side are PURE
VALUES since they have no
monad signature!
defer.info {
val int = run(List(1, 2))
val bool = run(List(true, false))
(int, bool)
}
// List((1, true), (1, false), (2, true), (2, false))
defer.info {
val bool = run(List(true, false))
val int = run(List(1, 2))
(int, bool)
}
// List((1, true), (2, true), (1, false), (2, false))
ARGUMENT 1 It is not Referentially Transparent
defer {
val int: Int = run(List(1, 2))
val bool: Boolean = run(List(true, false))
(int, bool)
}
// List((1, true), (1, false), (2, true), (2, false))
defer {
val bool: Boolean = run(List(true, false))
val int: Int = run(List(1, 2))
(int, bool)
}
// List((1, true), (2, true), (1, false), (2, false))
List(1, 2).flatMap { int =>
List(true, false).map { bool =>
(int, bool)
}
}
List(true, false).flatMap { int =>
List(1, 2).map { bool =>
(int, bool)
}
}
... but the things on the left
hand side are PURE VALUES
since they have no monad
signature!
No, they're just
syntactic sugar for flatMap/map
variables.
Same as the left-hand of a
for comprehension. Look...
ARGUMENT 1 It is not Referentially Transparent
List(1, 2).flatMap { int =>
List(true, false).map { bool =>
(int, bool)
}
}
List(true, false).flatMap { int =>
List(1, 2).map { bool =>
(int, bool)
}
}
for {
int: Int <- List(1, 2)
bool: Boolean <- List(true, false)
} yield (int, bool)
for {
bool: Boolean <- List(true, false)
int: Int <- List(1, 2)
} yield (int, bool)
defer {
val int: Int = run(List(1, 2))
val bool: Boolean = run(List(true, false))
(int, bool)
}
// List((1, true), (1, false), (2, true), (2, false))
defer {
val bool: Boolean = run(List(true, false))
val int: Int = run(List(1, 2))
(int, bool)
}
// List((1, true), (2, true), (1, false), (2, false))
ARGUMENT 1 It is not Referentially Transparent
List(1, 2).flatMap { int =>
List(true, false).map { bool =>
(int, bool)
}
}
List(true, false).flatMap { int =>
List(1, 2).map { bool =>
(int, bool)
}
}
No Equivalent
for {
bool: Boolean <- List(true, false)
int: Int <- List(1, 2)
} yield (int, bool)
defer {
val int: Int = run(List(1, 2))
(int, run(List(true, false)))
}
// List((1, true), (1, false), (2, true), (2, false))
defer {
val bool: Boolean = run(List(true, false))
val int: Int = run(List(1, 2))
(int, bool)
}
// List((1, true), (2, true), (1, false), (2, false))
ARGUMENT 1
defer {
val int = List(1, 2).run
val bool = List(true, false).run
val bool1 = bool
val int1 = int
(int, bool)
}
// List((1, true), (1, false), (2, true), (2, false))
defer {
val bool = List(true, false).run
val int = List(1, 2).run
val bool1 = bool
val int1 = int
(int, bool)
}
// List((1, true), (2, true), (1, false), (2, false))
It is not Referentially Transparent... Take 613
List(1, 2).flatMap { int =>
List(true, false).flatMap { bool =>
val bool1 = bool
val int1 = int
(int, bool)
}
}
List(true, false).flatMap { int =>
List(1, 2).flatMap { bool =>
val bool1 = bool
val int1 = int
(int, bool)
}
}
defer {
val int = List(1, 2)
val bool = List(true, false)
val bool1 = bool.run
val int1 = int.run
(int, bool)
}
// List((1, true), (2, true), (1, false), (2, false))
defer {
val bool = List(true, false)
val int = List(1, 2)
val bool1 = bool.run
val int1 = int.run
(int, bool)
}
// List((1, true), (2, true), (1, false), (2, false))
ARGUMENT 1
val int = List(1, 2)
val bool = List(true, false)
bool.flatMap { bool =>
int.map { int =>
(int, bool)
}
}
val bool = List(true, false)
val int = List(1, 2)
bool.flatMap { bool =>
int.map { int =>
(int, bool)
}
}
It is not Referentially Transparent... Take 613
ARGUMENT 2 It is Lawless
ARGUMENT 2 It is Lawless
I.
Ye shall not use mutable
things inside of a defer
unless the statement is
wrapped in a run(...) or
UNSAFE(...) block.
II.
Ye shall not use lazy things
inside of a defer unless the
statement is wrapped in a
run(...) or UNSAFE(...) block.
III.
Ye shall not interact with
external state inside of a
defer unless the statement is
wrapped in a run(...) or
unsafe(...) block.
IV.
All run(...) statements inside a
defer shall be run from top to
bottom if they are in different
lines, and from left to right if
they are on the same line.
V.
All plain statements inside of a
defer shall be run after the
run(...) on the line above and
before the run(...) on the line
below.
VI.
In the case that there are plain
statements and run(ZIO)s on the
same line, the run(ZIO)s can be
executed before the plain
statements in some
circumstances. To ensure
correctness in this scenario,
make sure to follow laws I, II,
and III.
Because ZIO actually has
them....
ERROR
CHANNELS
ZIO.fail(new Boom())
ZIO.succeed(pretendPureCode())
ERROR CHANNELS
💣 To Error Channel
💣 To Defect Channel
class Boom extends Exception("Boom!")
zio.direct.BoomExamples$Boom: Boom!
at zio.direct.BoomExamples$.main$$anonfun$1(BoomExamples.scala:22)
at zio.ZIO$.fail$$anonfun$1(ZIO.scala:3021)
at zio.ZIO$.failCause$$anonfun$1(ZIO.scala:3027)
at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:986)
zio.direct.BoomExamples$Boom: Boom!
at zio.direct.BoomExamples$.main$$anonfun$1(BoomExamples.scala:22)
at zio.UnsafeVersionSpecific.implicitFunctionIsFunction$$anonfun$1(UnsafeVersionSpecific.scala:23)
at zio.Unsafe$.unsafe(Unsafe.scala:37)
at zio.ZIOCompanionVersionSpecific.succeed$$anonfun$1(ZIOCompanionVersionSpecific.scala:161)
at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:831)
ZIO.succeed(pureCode)
< pureCode >
No Error
def pretendPureCode() = throw new Boom()
Expecting the somewhat/somewhat-not unexpected
ZIO.fail(new Boom())
ZIO.succeed(pretendPureCode())
ERROR CHANNELS
ZIO.attempt(pretendPureCode())
💣 To Error Channel
💣 To Defect Channel
💣 To Error Channel
class Boom extends Exception("Boom!")
zio.direct.BoomExamples$Boom: Boom!
at zio.direct.BoomExamples$.main$$anonfun$1(BoomExamples.scala:22)
at zio.ZIO$.fail$$anonfun$1(ZIO.scala:3021)
at zio.ZIO$.failCause$$anonfun$1(ZIO.scala:3027)
at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:986)
zio.direct.BoomExamples$Boom: Boom!
at zio.direct.BoomExamples$.main$$anonfun$1(BoomExamples.scala:22)
at zio.UnsafeVersionSpecific.implicitFunctionIsFunction$$anonfun$1(UnsafeVersionSpecific.scala:23)
at zio.Unsafe$.unsafe(Unsafe.scala:37)
at zio.ZIOCompanionVersionSpecific.succeed$$anonfun$1(ZIOCompanionVersionSpecific.scala:161)
at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:831)
zio.direct.BoomExamples$Boom: Boom!
at zio.direct.BoomExamples$.main$$anonfun$1(BoomExamples.scala:22)
at zio.ZIOCompanionVersionSpecific.attempt$$anonfun$1(ZIOCompanionVersionSpecific.scala:107)
at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:967)
ZIO.succeed(pureCode)
< pureCode >
No Error
def pretendPureCode() = throw new Boom()
Expecting Attempting the somewhat/somewhat-not unexpected
ZIO.fail(new Boom())
ZIO.succeed(pretendPureCode())
ERROR CHANNELS
ZIO.attempt(pretendPureCode())
💣 To Error Channel
💣 To Defect Channel
💣 To Error Channel
class Boom extends Exception("Boom!")
ZIO.succeed(pureCode)
No Error
def pretendPureCode() = throw new Boom()
defer { pureCode }
defer { throw new Boom() }
???
defer { pretendPureCode() }
Expecting Attempting the somewhat/somewhat-not unexpected
ZIO.fail(new Boom())
ZIO.succeed(pretendPureCode())
ERROR CHANNELS
ZIO.attempt(pretendPureCode())
💣 To Error Channel
💣 To Defect Channel
💣 To Error Channel
class Boom extends Exception("Boom!")
ZIO.succeed(pureCode)
No Error
def pretendPureCode() = throw new Boom()
defer { pureCode }
defer { throw new Boom() }
defer { pretendPureCode() }
defer { ZIO.attempt(pretendPureCode()).run }
ZIO.fail(new Boom())
ZIO.succeed(pretendPureCode())
ERROR CHANNELS
ZIO.attempt(pretendPureCode())
💣 To Error Channel
💣 To Defect Channel
💣 To Error Channel
class Boom extends Exception("Boom!")
ZIO.succeed(pureCode)
No Error
def pretendPureCode() = throw new Boom()
defer { pureCode }
defer { throw new Boom() }
defer { pretendPureCode() }
defer { unsafe(pretendPureCode()) }
ERROR CHANNELS
object Database {
def openConnection(): ZIO[Scope, Throwable, Connection]
}
object S3Object {
def openInputStream(path: String): ZIO[Scope, Throwable, InputStream]
}
defer {
try {
val input = S3Object.openInputStream("foo/bar").run
val reader = InputStreamReader(input)
val conn = Database.openConnection().run
val ps = conn.prepareStatement("INSERT ? INTO Someplace")
ps.setClob(1, reader)
ps.execute()
} catch {
case e: IOException => handle(e).run
case e: SQLException => handle(e).run
}
}
A "Real Cloud World" Example
ERROR CHANNELS
object Database {
def openConnection(): ZIO[Scope, Throwable, Connection]
}
object S3Object {
def openInputStream(path: String): ZIO[Scope, Throwable, InputStream]
}
defer {
try {
val input = S3Object.openInputStream("foo/bar").run
val reader = InputStreamReader(input)
val conn = Database.openConnection().run
val ps = conn.prepareStatement("INSERT ? INTO Someplace")
ps.setClob(1, reader)
ps.execute()
} catch {
case e: IOException => handle(e).run
case e: SQLException => handle(e).run
}
}
Could Potentially Throw Something!
A "Real Cloud World" Example
ERROR CHANNELS
object Database {
def openConnection(): ZIO[Scope, Throwable, Connection]
}
object S3Object {
def openInputStream(path: String): ZIO[Scope, Throwable, InputStream]
}
defer {
try {
val input = S3Object.openInputStream("foo/bar").run
val reader = unsafe(InputStreamReader(input))
val conn = Database.openConnection().run
val ps = unsafe(conn.prepareStatement("INSERT ? INTO Someplace"))
unsafe {
ps.setClob(1, reader)
ps.execute()
}
} catch {
case e: IOException => handle(e).run
case e: SQLException => handle(e).run
}
}
A "Real Cloud World" Example
ERROR CHANNELS
object Database {
def openConnection(): ZIO[Scope, Throwable, Connection]
}
object S3Object {
def openInputStream(path: String): ZIO[Scope, Throwable, InputStream]
}
defer {
try {
val input = S3Object.openInputStream("foo/bar").run
val reader = unsafe(InputStreamReader(input))
val conn = Database.openConnection().run
val ps = unsafe(conn.prepareStatement("INSERT ? INTO Someplace"))
unsafe {
ps.setClob(1, reader)
ps.execute()
}
} catch {
case e: IOException => handle(e).run
case e: SQLException => handle(e).run
}
}
A "Real Cloud World" Example
val out: ZIO[Scope, Throwable, Unit] =
ERROR CHANNELS
object Database {
def openConnection(): ZIO[Scope, Throwable, Connection]
}
object S3Object {
def openInputStream(path: String): ZIO[Scope, Throwable, InputStream]
}
defer {
try {
unsafe {
val input = S3Object.openInputStream("foo/bar").run
val reader = InputStreamReader(input)
val conn = Database.openConnection().run
val ps = conn.prepareStatement("INSERT ? INTO Someplace")
ps.setClob(1, reader)
ps.execute()
}
} catch {
case e: IOException => handle(e).run
case e: SQLException => handle(e).run
}
}
A "Real Cloud World" Example
Or wrap the entire thing!
ERROR CHANNELS
defer {
try {
unsafe {
val input = S3Object.openInputStream("foo/bar").run
val reader = InputStreamReader(input)
val conn = Database.openConnection().run
val ps = conn.prepareStatement("INSERT ? INTO Someplace")
ps.setClob(1, reader)
ps.execute()
}
} catch {
case e: IOException => handle(e).run
case e: SQLException => handle(e).run
}
}
A "Real Cloud World" Example
=== Reconstituted Code ========
S3Object.openInputStream("foo/bar").flatMap(((v: InputStream) => {
val input: InputStream = v
val reader: InputStreamReader = new InputStreamReader(input)
Database.openConnection.map(((`v₂`: Connection) => {
val conn: Connection = `v₂`
val ps: PreparedStatement = conn.prepareStatement("INSERT ? INTO Someplace")
ps.setClob(1, reader)
ps.execute
}))
})).catchSome(((tryLamParam: Throwable) => tryLamParam match {
case e: IOException =>
handle(e)
case e: SQLException =>
handle(`e₂`)
}))
=== Reconstituted Code ========
ObjectModel.S3Object.openInputStream("foo/bar").flatMap(((v: InputStream) => {
val input: InputStream = v
ZIO.attempt(new InputStreamReader(input)).flatMap(((`v₂`: InputStreamReader) => {
val reader: InputStreamReader = `v₂`
ObjectModel.Database.openConnection.flatMap(((`v₃`: Connection) => {
val conn: Connection = `v₃`
ZIO.attempt(conn.prepareStatement("INSERT ? INTO Someplace")).flatMap(((`v₄`: PreparedStatement) => {
val ps: PreparedStatement = `v₄`
ZIO.attempt({
ps.setClob(1, reader)
ps.execute
})
}))
}))
}))
})).catchSome(((tryLamParam: Throwable) => tryLamParam match {
case e: IOException =>
ObjectModel.handle(e)
case e: SQLException =>
ObjectModel.handle(`e₂`)
}))
Wrap in Unsafe
Generated Code wraps ZIO.attempt
USING REFS
var i: Int = 10
while (i > 0) {
println(s"Currently: ${i}")
i = i - 1
}
Looping with vars Looping with Refs
Okay...
?
USING REFS
var i: Int = 10
while (i > 0) {
println(s"Currently: ${i}")
i = i - 1
}
Looping with vars Looping with Refs
val i: UIO[Ref[Int]] = Ref.make(10)
i.flatMap { i0 =>
def whileLoop(): ZIO[Any, Nothing, Unit] =
i0.get.flatMap { iv =>
if (iv > 0) {
println(s"Currently: ${iv}")
i0.update(i => i - 1).flatMap { _ =>
whileLoop()
}
} else {
ZIO.unit
}
}
whileLoop()
}
Let's pull some teeth!
USING REFS
var i: Int = 10
while (i > 0) {
println(s"Currently: ${i}")
i = i - 1
}
Looping with vars Looping with Refs
val i: UIO[Ref[Int]] = Ref.make(10)
i.flatMap { i0 =>
def whileLoop(): ZIO[Any, Nothing, Unit] =
i0.get.flatMap { iv =>
if (iv > 0) {
println(s"Currently: ${iv}")
i0.update(i => i - 1).flatMap { _ =>
whileLoop()
}
} else {
ZIO.unit
}
}
whileLoop()
}
Let's pull some teeth!
Be sure these are right...
or it runs forever!
USING REFS
var i: Int = 10
while (i > 0) {
println(s"Currently: ${i}")
i = i - 1
}
Looping with vars Looping with Refs - Take 2
defer {
val i: Ref[Int] = Ref.make(10).run
while (i.get.run > 0) {
println(s"Currently: ${i.get.run}")
i.update(i => i - 1).run
}
}
USING REFS
var i: Int = 10
while (i > 0) {
println(s"Currently: ${i}")
i = i - 1
}
Looping with vars Looping with Refs - Take 2
defer.info {
val i: Ref[Int] = Ref.make(10).run
while (i.get.run > 0) {
println(s"Currently: ${i.get.run}")
i.update(i => i - 1).run
}
}
=== Reconstituted Code ========
Ref.make(10).flatMap(((v: Ref[Int]) => {
val i: Ref[Int] = v
def whileFunc: ZIO[Any, Nothing, Unit] = i.get.map(((sm: Int) => {
val runVal: Int = sm
runVal.>(0)
})).flatMap(((`v₂`: Boolean) => {
val ifVar: Boolean = `v₂`
if (ifVar) i.get.map(((`sm₂`: Int) => {
val `runVal₂`: Int = `sm₂`
println(_root_.scala.StringContext.apply("Currently: ", "").s(`runVal₂`))
})).flatMap(((`v₃`: Unit) => i.update(((`i₂`: Int) => `i₂`.-(1))))).flatMap(((`v₄`: Any) => whileFunc)) else zio.ZIO.succeed(())
}))
whileFunc
}))
USING REFS
Quicksort with Mutable Indices
def quicksortImperative(a: Array[Int]): Unit = {
def swap(i: Int, j: Int): Unit = {
val t = a(i)
a(i) = a(j)
a(j) = t
}
def sort(l: Int, r: Int): Unit = {
val pivot = a((l + r) / 2)
var i = l
var j = r
while (i <= j) {
while (a(i) < pivot) i += 1
while (a(j) > pivot) j -= 1
if (i <= j) {
swap(i, j)
i += 1
j -= 1
}
}
if (l < j) sort(l, j)
if (j < r) sort(i, r)
}
sort(0, a.length - 1)
}
val arr = Array(3, 2, 8, 5, 7, 2, 3, 8, 9, 4, 5, 8, 2, 3, 4, 7, 6, 5, 9, 2, 3, 8, 4, 7, 5, 6, 2, 0, 8, 3)
quicksortImperative(arr)
println(arr.toList)
// List(0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9)
Quicksort with Mutable Indices
USING REFS
Quicksort with Mutable Indices
def quicksortImperative(a: Array[Int]): Unit = {
def swap(i: Int, j: Int): Unit = {
val t = a(i)
a(i) = a(j)
a(j) = t
}
def sort(l: Int, r: Int): Unit = {
val pivot = a((l + r) / 2)
var i = l
var j = r
while (i <= j) {
while (a(i) < pivot) i += 1
while (a(j) > pivot) j -= 1
if (i <= j) {
swap(i, j)
i += 1
j -= 1
}
}
if (l < j) sort(l, j)
if (j < r) sort(i, r)
}
sort(0, a.length - 1)
}
val arr = Array(3, 2, 8, 5, 7, 2, 3, 8, 9, 4, 5, 8, 2, 3, 4, 7, 6, 5, 9, 2, 3, 8, 4, 7, 5, 6, 2, 0, 8, 3)
runUnsafe(quicksortDefer(arr))
println(arr.toList)
// List(0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9)
Quicksort with Mutable Indices
def quicksortDefer(arr: Array[Int]): ZIO[Any, Nothing, Unit] = {
def swap(i: Int, j: Int) =
val temp = arr(i)
arr(i) = arr(j)
arr(j) = temp
def sort(l: Int, r: Int): ZIO[Any, Nothing, Unit] =
defer(Params(Verify.Lenient)) {
val pivot = arr((l + r) / 2)
val i = Ref.make(l).run
val j = Ref.make(r).run
while (i.get.run <= j.get.run)
while (arr(i.get.run) < pivot) i.getAndUpdate(i => i + 1).run
while (arr(j.get.run) > pivot) j.getAndUpdate(j => j - 1).run
if (i.get.run <= j.get.run)
swap(i.get.run, j.get.run)
i.getAndUpdate(i => i + 1).run
j.getAndUpdate(j => j - 1).run
if (l < j.get.run)
val jv = j.get.run
sort(l, jv).run
if (j.get.run < r)
val iv = i.get.run
sort(iv, r).run
}
sort(0, arr.length - 1)
}
IN SUMMARY...
1. Introducing
ZIO-DIRECT is a ZIO-specific system
based on the Monadless paradigm of
imperative -> functional transformation.
2. Uses ZIO Features
ZIO-DIRECT Correctly interoperates
with ZIO Environment
and Error Types
3. Can Peek Inside
ZIO-DIRECT provides enhanced
feedback with .info and .verbose so
that you clearly know what code it is
generating.
4. Correctness based on Constraints
ZIO-DIRECT constrains problematic
Scala constructs in order to
guarantee correctness.
5. Uses Error Channel
ZIO-DIRECT uses the ZIO error
channel to mediate try-catch
statements.
6. Works well with Mutable Refs
ZIO-DIRECT makes the use of ZIO
Refs natural and ergonomic.
Future Directions
val q: Future[(Person, Address)] =
defer[Future].from {
val p = httpGet[Person]("http://people").run
val a = httpGet[Address](s"http://address?owner=${p.id}").run
(p, a)
}
def httpGet[T](str: String): Future[T]
val q: List[(Person, Address)] =
defer[List].from {
val p = people.run
val a = p.addresses.run
(p, a)
}
def people: List[Person]
val q: Query[(Person, Address)] =
defer[Query].from {
val p = query[Person].run
val a = query[Address].join(a => a.fk == p.id).run
(p, a)
}
def query[T]: Query[T]
Thank You
Repo: github.com/zio/zio-direct
Example Usage: github.com/zio/zio-protoquill/tree/zio-direct

Mais conteúdo relacionado

Mais procurados

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
 
Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in ScalaHermann Hueck
 
Monoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and CatsMonoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and CatsPhilip Schwarz
 
Running Free with the Monads
Running Free with the MonadsRunning Free with the Monads
Running Free with the Monadskenbot
 
A Prelude of Purity: Scaling Back ZIO
A Prelude of Purity: Scaling Back ZIOA Prelude of Purity: Scaling Back ZIO
A Prelude of Purity: Scaling Back ZIOJorge Vásquez
 
Functional Error Handling with Cats
Functional Error Handling with CatsFunctional Error Handling with Cats
Functional Error Handling with CatsMark Canlas
 
Embedding Generic Monadic Transformer into Scala. [Tfp2022]
Embedding Generic Monadic Transformer into Scala. [Tfp2022]Embedding Generic Monadic Transformer into Scala. [Tfp2022]
Embedding Generic Monadic Transformer into Scala. [Tfp2022]Ruslan Shevchenko
 
Functional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorldFunctional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorldJorge Vásquez
 
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
 
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
 
One Monad to Rule Them All
One Monad to Rule Them AllOne Monad to Rule Them All
One Monad to Rule Them AllJohn De Goes
 
Peeking inside the engine of ZIO SQL.pdf
Peeking inside the engine of ZIO SQL.pdfPeeking inside the engine of ZIO SQL.pdf
Peeking inside the engine of ZIO SQL.pdfJaroslavRegec1
 
Purely Functional Data Structures in Scala
Purely Functional Data Structures in ScalaPurely Functional Data Structures in Scala
Purely Functional Data Structures in ScalaVladimir Kostyukov
 
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
 
non-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parametersnon-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parametersPhilip Schwarz
 
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
 
Http4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackHttp4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackGaryCoady
 
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
 

Mais procurados (20)

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
 
Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in Scala
 
Monoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and CatsMonoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and Cats
 
Running Free with the Monads
Running Free with the MonadsRunning Free with the Monads
Running Free with the Monads
 
A Prelude of Purity: Scaling Back ZIO
A Prelude of Purity: Scaling Back ZIOA Prelude of Purity: Scaling Back ZIO
A Prelude of Purity: Scaling Back ZIO
 
Functional Error Handling with Cats
Functional Error Handling with CatsFunctional Error Handling with Cats
Functional Error Handling with Cats
 
Embedding Generic Monadic Transformer into Scala. [Tfp2022]
Embedding Generic Monadic Transformer into Scala. [Tfp2022]Embedding Generic Monadic Transformer into Scala. [Tfp2022]
Embedding Generic Monadic Transformer into Scala. [Tfp2022]
 
Functional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorldFunctional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorld
 
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
 
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...
 
One Monad to Rule Them All
One Monad to Rule Them AllOne Monad to Rule Them All
One Monad to Rule Them All
 
Peeking inside the engine of ZIO SQL.pdf
Peeking inside the engine of ZIO SQL.pdfPeeking inside the engine of ZIO SQL.pdf
Peeking inside the engine of ZIO SQL.pdf
 
Java 8 Workshop
Java 8 WorkshopJava 8 Workshop
Java 8 Workshop
 
jQuery PPT
jQuery PPTjQuery PPT
jQuery PPT
 
Purely Functional Data Structures in Scala
Purely Functional Data Structures in ScalaPurely Functional Data Structures in Scala
Purely Functional Data Structures in Scala
 
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...
 
non-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parametersnon-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parameters
 
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...
 
Http4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackHttp4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web Stack
 
Boost your productivity with Scala tooling!
Boost your productivity  with Scala tooling!Boost your productivity  with Scala tooling!
Boost your productivity with Scala tooling!
 

Semelhante a ZIO-Direct - Functional Scala 2022

Generic Functional Programming with Type Classes
Generic Functional Programming with Type ClassesGeneric Functional Programming with Type Classes
Generic Functional Programming with Type ClassesTapio Rautonen
 
Scala for Java Developers - Intro
Scala for Java Developers - IntroScala for Java Developers - Intro
Scala for Java Developers - IntroDavid Copeland
 
2.2 higher order-functions
2.2 higher order-functions2.2 higher order-functions
2.2 higher order-functionsfuturespective
 
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
 
(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
 
Post-Free: Life After Free Monads
Post-Free: Life After Free MonadsPost-Free: Life After Free Monads
Post-Free: Life After Free MonadsJohn De Goes
 
Introduction to Python
Introduction to PythonIntroduction to Python
Introduction to PythonUC San Diego
 
3 kotlin vs. java- what kotlin has that java does not
3  kotlin vs. java- what kotlin has that java does not3  kotlin vs. java- what kotlin has that java does not
3 kotlin vs. java- what kotlin has that java does notSergey Bandysik
 
GE8151 Problem Solving and Python Programming
GE8151 Problem Solving and Python ProgrammingGE8151 Problem Solving and Python Programming
GE8151 Problem Solving and Python ProgrammingMuthu Vinayagam
 
Functions In Scala
Functions In Scala Functions In Scala
Functions In Scala Knoldus Inc.
 
Lambda functions in java 8
Lambda functions in java 8Lambda functions in java 8
Lambda functions in java 8James Brown
 
ITT 2015 - Saul Mora - Object Oriented Function Programming
ITT 2015 - Saul Mora - Object Oriented Function ProgrammingITT 2015 - Saul Mora - Object Oriented Function Programming
ITT 2015 - Saul Mora - Object Oriented Function ProgrammingIstanbul Tech Talks
 
From Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risksFrom Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risksSeniorDevOnly
 
Fp in scala part 1
Fp in scala part 1Fp in scala part 1
Fp in scala part 1Hang Zhao
 

Semelhante a ZIO-Direct - Functional Scala 2022 (20)

Generic Functional Programming with Type Classes
Generic Functional Programming with Type ClassesGeneric Functional Programming with Type Classes
Generic Functional Programming with Type Classes
 
Scala for Java Developers - Intro
Scala for Java Developers - IntroScala for Java Developers - Intro
Scala for Java Developers - Intro
 
2.2 higher order-functions
2.2 higher order-functions2.2 higher order-functions
2.2 higher order-functions
 
Scala Hands On!!
Scala Hands On!!Scala Hands On!!
Scala Hands On!!
 
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
 
(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?
 
Post-Free: Life After Free Monads
Post-Free: Life After Free MonadsPost-Free: Life After Free Monads
Post-Free: Life After Free Monads
 
ScalaBlitz
ScalaBlitzScalaBlitz
ScalaBlitz
 
Introduction to Python
Introduction to PythonIntroduction to Python
Introduction to Python
 
3 kotlin vs. java- what kotlin has that java does not
3  kotlin vs. java- what kotlin has that java does not3  kotlin vs. java- what kotlin has that java does not
3 kotlin vs. java- what kotlin has that java does not
 
GE8151 Problem Solving and Python Programming
GE8151 Problem Solving and Python ProgrammingGE8151 Problem Solving and Python Programming
GE8151 Problem Solving and Python Programming
 
Functions In Scala
Functions In Scala Functions In Scala
Functions In Scala
 
Lambda functions in java 8
Lambda functions in java 8Lambda functions in java 8
Lambda functions in java 8
 
ITT 2015 - Saul Mora - Object Oriented Function Programming
ITT 2015 - Saul Mora - Object Oriented Function ProgrammingITT 2015 - Saul Mora - Object Oriented Function Programming
ITT 2015 - Saul Mora - Object Oriented Function Programming
 
Scala best practices
Scala best practicesScala best practices
Scala best practices
 
From Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risksFrom Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risks
 
Fp in scala part 1
Fp in scala part 1Fp in scala part 1
Fp in scala part 1
 

Último

How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerThousandEyes
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about usDynamic Netsoft
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providermohitmore19
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...MyIntelliSource, Inc.
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfkalichargn70th171
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsJhone kinadey
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsAndolasoft Inc
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comFatema Valibhai
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
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
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionSolGuruz
 

Último (20)

How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about us
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
Exploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the ProcessExploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the Process
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
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-...
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with Precision
 

ZIO-Direct - Functional Scala 2022

  • 3. Is Functional Programming (With Effect Systems) Easy?
  • 4. IMPERATIVE SEQUENCING val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) def read(file: File): String def write(file: File, content: String): Unit
  • 5. IMPERATIVE FUNCTIONAL SEQUENCING val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) def read(file: File): String def write(file: File, content: String): Unit def read(file: File): ZIO[Any, Throwable, String] def write(file: File, content: String): ZIO[Any, Throwable, Unit]
  • 6. IMPERATIVE FUNCTIONAL SEQUENCING val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) def read(file: File): String def write(file: File, content: String): Unit def read(file: File): ZIO[Any, Throwable, String] def write(file: File, content: String): ZIO[Any, Throwable, Unit] read(fileA).flatMap { textA => read(fileB).flatMap { textB => write(fileC, textA + textB) } }
  • 7. IMPERATIVE FUNCTIONAL SEQUENCING val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) def read(file: File): String def write(file: File, content: String): Unit def read(file: File): ZIO[Any, Throwable, String] def write(file: File, content: String): ZIO[Any, Throwable, Unit] for { textA <- read(fileA) textB <- read(fileB) _ <- write(fileC, textA + textB) } yield () FOR COMPREHENSIONS TO THE RESCUE
  • 8. IMPERATIVE FUNCTIONAL SEQUENCING val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) def read(file: File): String def write(file: File, content: String): Unit def read(file: File): ZIO[Any, Throwable, String] def write(file: File, content: String): ZIO[Any, Throwable, Unit] for { textA <- read(fileA) textB <- read(fileB) _ <- write(fileC, textA + textB) } yield () FOR COMPREHENSIONS TO THE RESCUE For-Comprehension All the things!
  • 9. IMPERATIVE BRANCHING val rows: List[Row] = ... val db = Database.open() if (db.transactionsEnabled() && db.lazyFetchEnabled()) { db.bulkInsert(rows) } class Database { def transactionsEnabled(): Boolean def lazyFetchEnabled(): Boolean def bulkInsert(row: List[Row]): Unit }
  • 10. IMPERATIVE FUNCTIONAL BRANCHING FOR COMPREHENSIONS TO THE RESCUE? class Database { def transactionsEnabled(): ZIO[Any, Throwable, Boolean] def lazyFetchEnabled(): ZIO[Any, Throwable, Boolean] def bulkInsert(row: List[Row]): ZIO[Any, Throwable, Unit] } val rows: List[Row] = ... val db = Database.open() if (db.transactionsEnabled() && db.lazyFetchEnabled()) { db.bulkInsert(rows) } val rows: List[Row] = ... for { db <- Database.open te <- db.transactionsEnabled() lf <- db.lazyFetchEnabled() _ <- { if (te && lf) db.bulkInsert(rows) else ZIO.unit } } yield () Why am I fetching properties I might never use??? class Database { def transactionsEnabled(): Boolean def lazyFetchEnabled(): Boolean def bulkInsert(row: List[Row]): Unit }
  • 11. IMPERATIVE FUNCTIONAL BRANCHING FOR COMPREHENSIONS TO THE RESCUE? class Database { def transactionsEnabled(): ZIO[Any, Throwable, Boolean] def lazyFetchEnabled(): ZIO[Any, Throwable, Boolean] def bulkInsert(row: List[Row]): ZIO[Any, Throwable, Unit] } val rows: List[Row] = ... val db = Database.open() if (db.transactionsEnabled() && db.lazyFetchEnabled()) { db.bulkInsert(rows) } class Database { def transactionsEnabled(): Boolean def lazyFetchEnabled(): Boolean def bulkInsert(row: List[Row]): Unit } val rows: List[Row] = ... Database.open().flatMap { db => db.transactionsEnabled().flatMap { te => if (te) db.lazyFetchEnabled().flatMap { lf => if (lf) db.bulkInsert(rows) else ZIO.unit } else ZIO.unit } } Back to This???
  • 12. IMPERATIVE FUNCTIONAL LOOPING class File { def readLine(): String def close(): Unit } var line: String = file.readLine() while (line != null) { buffer.append(line) line = file.readLine() }
  • 13. IMPERATIVE FUNCTIONAL THE PROGRAM... OR YOUR MIND? class File { def readLine(): String def close(): Unit } var line: String = file.readLine() while (line != null) { buffer.append(line) line = file.readLine() } class File { def readLine(): ZIO[Any, Throwable, String] def close(): ZIO[Any, Throwable, Unit] } file.readLine().flatMap { line0 => var line = line0 def whileFun(): ZIO[Any, Throwable, Unit] = if (line != null) buffer.append(line) file.readLine().flatMap { lineN => line = lineN whileFun() } else ZIO.unit whileFun() Wait... there are actually cases where WHILE is not reducible to simple iteration examples??? LOOPING
  • 14. IMPERATIVE FUNCTIONAL var line: String = file.readLine() def whileFun(): Unit = if (line != null) { buffer.append(line) line = file.readLine() whileFun() } else { () } class File { def readLine(): String def close(): Unit } class File { def readLine(): ZIO[Any, Throwable, String] def close(): ZIO[Any, Throwable, Unit] } file.readLine().flatMap { line0 => var line = line0 def whileFun(): ZIO[Any, Throwable, Unit] = if (line != null) buffer.append(line) file.readLine().flatMap { lineN => line = lineN whileFun() } else ZIO.unit whileFun() THE PROGRAM... OR YOUR MIND? LOOPING
  • 15. IMPERATIVE FUNCTIONAL LOOPING def higherFunction(file: File) = { var line: String = file.readLine() def whileFun(): Unit = if (line != null) { buffer.append(line) line = file.readLine() whileFun() } else { () } } P.S. NOT ACTUALLY USING GLOBALLY MUTABLE STATE! class File { def readLine(): String def close(): Unit } class File { def readLine(): ZIO[Any, Throwable, String] def close(): ZIO[Any, Throwable, Unit] } def higherFunction(file: File) = { file.readLine().flatMap { line0 => var line = line0 def whileFun(): ZIO[Any, Throwable, Unit] = if (line != null) buffer.append(line) file.readLine().flatMap { lineN => line = lineN whileFun() } else ZIO.unit whileFun() }
  • 16. IMPERATIVE ERROR HANDLING EXPECT THE (MILDLY) UNEXPECTED var file: JsonFile = JsonFile.from(path) try { file = file.readToJson() } catch { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) } finally { file.close() } class JsonFile { def readToJson(): Json def close(): Unit } object JsonFile { def from(path: String): JsonFile }
  • 17. IMPERATIVE FUNCTIONAL ERROR HANDLING EXPECT THE (MILDLY) UNEXPECTED var file: JsonFile = JsonFile.from(path) try { file = file.readToJson() } catch { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) } finally { file.close() } succeed(JsonFile.from(path)).flatMap { jsonFile => jsonFile.readToJson() }.catchSome { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) }.ensuring { jsonFile.close().orDie } class JsonFile { def readToJson(): ZIO[Any, Throwable, Json] def close(): ZIO[Any, Throwable, Unit] } object JsonFile { def from(path: String): JsonFile } class JsonFile { def readToJson(): Json def close(): Unit } object JsonFile { def from(path: String): JsonFile }
  • 18. Once upon a time... IN JAVA 101 val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) if ( db.transactionsEnabled() && db.lazyFetchEnabled() ) db.bulkInsert(rows) } while (line != null) { buffer.append(line) line = file.readLine() } try { file = JsonFile.open(path) file.readToJson() } catch { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) } finally { file.close() }
  • 19. But these don't scale val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) if ( db.transactionsEnabled() && db.lazyFetchEnabled() ) db.bulkInsert(rows) } while (line != null) { buffer.append(line) line = file.readLine() } val file = JsonFile.from(path) try { file.readToJson() } catch { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) } finally { file.close() }
  • 20. val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) if ( db.transactionsEnabled() && db.lazyFetchEnabled() ) db.bulkInsert(rows) } while (line != null) { buffer.append(line) line = file.readLine() } val file = JsonFile.from(path) try { file.readToJson() } catch { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) } finally { file.close() } But these don't scale SO WE NEED THESE... file.readLine().flatMap { line0 => var line = line0 def whileFun(): ZIO[Any, Throwable, Unit] = if (line != null) buffer.append(line) file.readLine().flatMap { lineN => line = lineN whileFun() } else ZIO.unit whileFun() db.transactionsEnabled().flatMap { te => if (te) db.lazyFetchEnabled().flatMap { lf => if (lf) db.bulkInsert(rows) else ZIO.unit } else ZIO.unit } succeed(JsonFile.from(path)).flatMap { jsonFile => jsonFile.readToJson() }.catchSome { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) }.ensuring { jsonFile.close().orDie } for { textA <- read(fileA) textB <- read(fileB) _ <- write(fileC, textA + textB) } yield ()
  • 21. val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) if ( db.transactionsEnabled() && db.lazyFetchEnabled() ) db.bulkInsert(rows) } while (line != null) { buffer.append(line) line = file.readLine() } try { file = JsonFile.open(path) file.readToJson() } catch { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) } finally { file.close() } But these don't scale OR DO They? file.readLine().flatMap { line0 => var line = line0 def whileFun(): ZIO[Any, Throwable, Unit] = if (line != null) buffer.append(line) file.readLine().flatMap { lineN => line = lineN whileFun() } else ZIO.unit whileFun() db.transactionsEnabled().flatMap { te => if (te) db.lazyFetchEnabled().flatMap { lf => if (lf) db.bulkInsert(rows) else ZIO.unit } else ZIO.unit } for { textA <- read(fileA) textB <- read(fileB) _ <- write(fileC, textA + textB) } yield () succeed(JsonFile.from(path)).flatMap { jsonFile => jsonFile.readToJson() }.catchSome { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) }.ensuring { jsonFile.close().orDie }
  • 22. val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) if ( db.transactionsEnabled() && db.lazyFetchEnabled() ) db.bulkInsert(rows) } while (line != null) { buffer.append(line) line = file.readLine() } try { file = JsonFile.open(path) file.readToJson() } catch { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) } finally { file.close() } file.readLine().flatMap { line0 => var line = line0 def whileFun(): ZIO[Any, Throwable, Unit] = if (line != null) buffer.append(line) file.readLine().flatMap { lineN => line = lineN whileFun() } else ZIO.unit whileFun() db.transactionsEnabled().flatMap { te => if (te) db.lazyFetchEnabled().flatMap { lf => if (lf) db.bulkInsert(rows) else ZIO.unit } else ZIO.unit } for { textA <- read(fileA) textB <- read(fileB) _ <- write(fileC, textA + textB) } yield () WHAT IF I OLD YOU THAT MONADIC SYNTAX IS JUST INCIDENTAL COMPLEXITY? succeed(JsonFile.from(path)).flatMap { jsonFile => jsonFile.readToJson() }.catchSome { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) }.ensuring { jsonFile.close().orDie }
  • 23. val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) if ( db.transactionsEnabled() && db.lazyFetchEnabled() ) db.bulkInsert(rows) } while (line != null) { buffer.append(line) line = file.readLine() } try { file = JsonFile.open(path) file.readToJson() } catch { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) } finally { file.close() } file.readLine().flatMap { line0 => var line = line0 def whileFun(): ZIO[Any, Throwable, Unit] = if (line != null) buffer.append(line) file.readLine().flatMap { lineN => line = lineN whileFun() } else ZIO.unit whileFun() db.transactionsEnabled().flatMap { te => if (te) db.lazyFetchEnabled().flatMap { lf => if (lf) db.bulkInsert(rows) else ZIO.unit } else ZIO.unit } for { textA <- read(fileA) textB <- read(fileB) _ <- write(fileC, textA + textB) } yield () succeed(JsonFile.from(path)).flatMap { jsonFile => jsonFile.readToJson() }.catchSome { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) }.ensuring { jsonFile.close().orDie } Imperative coding is easy. Write imperative style, translate to Functional! Functional code is scaleable.
  • 24. val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) if ( db.transactionsEnabled() && db.lazyFetchEnabled() ) db.bulkInsert(rows) } while (line != null) { buffer.append(line) line = file.readLine() } try { file = JsonFile.open(path) file.readToJson() } catch { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) } finally { file.close() } file.readLine().flatMap { line0 => var line = line0 def whileFun(): ZIO[Any, Throwable, Unit] = if (line != null) buffer.append(line) file.readLine().flatMap { lineN => line = lineN whileFun() } else ZIO.unit whileFun() db.transactionsEnabled().flatMap { te => if (te) db.lazyFetchEnabled().flatMap { lf => if (lf) db.bulkInsert(rows) else ZIO.unit } else ZIO.unit } for { textA <- read(fileA) textB <- read(fileB) _ <- write(fileC, textA + textB) } yield () Monadless succeed(JsonFile.from(path)).flatMap { jsonFile => jsonFile.readToJson() }.catchSome { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) }.ensuring { jsonFile.close().orDie }
  • 25. RE-WRITING SEQUENCES FROM THIS... TO THIS! FUNCTIONAL IMPERATIVE val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) def read(file: File): String def write(file: File, content: String): Unit read(fileA).flatMap { textA => read(fileB).flatMap { textB => write(fileC, textA + textB) } } def read(file: File): ZIO[Any, Throwable, String] def write(file: File, content: String): ZIO[Any, Throwable, Unit]
  • 26. FUNCTIONAL IMPERATIVE val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) def read(file: File): ZIO[Any, Throwable, String] def write(file: File, content: String): ZIO[Any, Throwable, Unit] read(fileA).flatMap { textA => read(fileB).flatMap { textB => write(fileC, textA + textB) } } val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) RE-WRITING SEQUENCES MAKE TO LOOK LIKE IMPERATIVE def read(file: File): String def write(file: File, content: String): Unit
  • 27. FUNCTIONAL IMPERATIVE val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) def read(file: File): String def write(file: File, content: String): Unit def read(file: File): ZIO[Any, Throwable, String] def write(file: File, content: String): ZIO[Any, Throwable, Unit] read(fileA).flatMap { textA => read(fileB).flatMap { textB => write(fileC, textA + textB) } } val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) :ZIO[..String] :ZIO[..String] :ZIO[..Unit] RE-WRITING SEQUENCES ...BUT TYPE LIKE FUNCTINOAL
  • 28. RE-WRITING SEQUENCES FUNCTIONAL IMPERATIVE val textA = run(read(fileA)) val textB = run(read(fileB)) run(write(fileC, textA + textB)) def read(file: File): String def write(file: File, content: String): Unit def read(file: File): ZIO[Any, Throwable, String] def write(file: File, content: String): ZIO[Any, Throwable, Unit] PRESTO-CHANGE-OH read(fileA).flatMap { textA => read(fileB).flatMap { textB => write(fileC, textA + textB) } } val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) :String :String :Unit
  • 29. RE-WRITING SEQUENCES FUNCTIONAL IMPERATIVE val textA = run(read(fileA)) val textB = run(read(fileB)) run(write(fileC, textA + textB)) def read(file: File): String def write(file: File, content: String): Unit def read(file: File): ZIO[Any, Throwable, String] def write(file: File, content: String): ZIO[Any, Throwable, Unit] read(fileA).flatMap { textA => read(fileB).flatMap { textB => write(fileC, textA + textB) } } val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) THE RABIT IN THE HAT :String :String :Unit
  • 30. FUNCTIONAL IMPERATIVE defer { val textA = run(read(fileA)) val textB = run(read(fileB)) run(write(fileC, textA + textB)) } def read(file: File): String def write(file: File, content: String): Unit def read(file: File): ZIO[Any, Throwable, String] def write(file: File, content: String): ZIO[Any, Throwable, Unit] read(fileA).flatMap { textA => read(fileB).flatMap { textB => write(fileC, textA + textB) } } val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) RE-WRITING SEQUENCES RE-WRITING SEQUENCES RUN MACRO, WRAP IN A BOW
  • 31. RE-WRITING SEQUENCES RE-WRITING SEQUENCES FUNCTIONAL IMPERATIVE defer { val textA = run(read(fileA)) val textB = run(read(fileB)) run(write(fileC, textA + textB)) } def read(file: File): String def write(file: File, content: String): Unit def read(file: File): ZIO[Any, Throwable, String] def write(file: File, content: String): ZIO[Any, Throwable, Unit] read(fileA).flatMap { textA => read(fileB).flatMap { textB => write(fileC, textA + textB) } } val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) :ZIO[Any, Throwable, Unit] RUN MACRO, WRAP IN A BOW
  • 32. FUNCTIONAL IMPERATIVE val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) def read(file: File): String def write(file: File, content: String): Unit def read(file: File): ZIO[Any, Throwable, String] def write(file: File, content: String): ZIO[Any, Throwable, Unit] read(fileA).flatMap { textA => read(fileB).flatMap { textB => write(fileC, textA + textB) } } defer { val textA = run(read(fileA)) val textB = run(read(fileB)) run(write(fileC, textA + textB)) } RE-WRITING SEQUENCES FEEL IMPERATIVE ACT FUNCTIONALLY THAT'S ALL FOLKS
  • 33. RE-WRITING SEQUENCES P.S. BETTER LIKE THIS FUNCTIONAL IMPERATIVE defer { val textA = read(fileA).run val textB = read(fileB).run write(fileC, textA + textB).run } def read(file: File): String def write(file: File, content: String): Unit def read(file: File): ZIO[Any, Throwable, String] def write(file: File, content: String): ZIO[Any, Throwable, Unit] read(fileA).flatMap { textA => read(fileB).flatMap { textB => write(fileC, textA + textB) } } val textA = read(fileA) val textB = read(fileB) write(fileC, textA + textB) FEEL IMPERATIVE ACT FUNCTIONALLY
  • 34. RE-WRITING BRANCHING START WITH IMPERATIVE STYLE FUNCTIONAL IMPERATIVE class Database { def transactionsEnabled(): ZIO[Any, Throwable, Boolean] def lazyFetchEnabled(): ZIO[Any, Throwable, Boolean] def bulkInsert(row: List[Row]): ZIO[Any, Throwable, Unit] } val db = Database.open() if ( db.transactionsEnabled() && db.lazyFetchEnabled() ) { db.bulkInsert(rows) } class Database { def transactionsEnabled(): Boolean def lazyFetchEnabled(): Boolean def bulkInsert(row: List[Row]): Unit } Database.open().flatMap { db => db.transactionsEnabled().flatMap { te => if (te) db.lazyFetchEnabled().flatMap { lf => if (lf) db.bulkInsert(rows) else ZIO.unit } else ZIO.unit } } val db = Database.open() if ( db.transactionsEnabled() && db.lazyFetchEnabled() ) { db.bulkInsert(rows) }
  • 35. FUNCTIONAL IMPERATIVE class Database { def transactionsEnabled(): ZIO[Any, Throwable, Boolean] def lazyFetchEnabled(): ZIO[Any, Throwable, Boolean] def bulkInsert(row: List[Row]): ZIO[Any, Throwable, Unit] } defer { val db = Database.open().run if ( db.transactionsEnabled().run && db.lazyFetchEnabled().run ) { db.bulkInsert(rows).run } } val db = Database.open() if ( db.transactionsEnabled() && db.lazyFetchEnabled() ) { db.bulkInsert(rows) } class Database { def transactionsEnabled(): Boolean def lazyFetchEnabled(): Boolean def bulkInsert(row: List[Row]): Unit } Database.open().flatMap { db => db.transactionsEnabled().flatMap { te => if (te) db.lazyFetchEnabled().flatMap { lf => if (lf) db.bulkInsert(rows) else ZIO.unit } else ZIO.unit } } FEEL IMPERATIVE ACT FUNCTIONALLY RE-WRITING BRANCHING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN!
  • 36. FUNCTIONAL IMPERATIVE class Database { def transactionsEnabled(): ZIO[Any, Throwable, Boolean] def lazyFetchEnabled(): ZIO[Any, Throwable, Boolean] def bulkInsert(row: List[Row]): ZIO[Any, Throwable, Unit] } defer { val db = Database.open().run if ( db.transactionsEnabled().run && db.lazyFetchEnabled().run ) { db.bulkInsert(rows).run } } val db = Database.open() if ( db.transactionsEnabled() && db.lazyFetchEnabled() ) { db.bulkInsert(rows) } class Database { def transactionsEnabled(): Boolean def lazyFetchEnabled(): Boolean def bulkInsert(row: List[Row]): Unit } Database.open().flatMap { db => db.transactionsEnabled().flatMap { te => if (te) db.lazyFetchEnabled().flatMap { lf => if (lf) db.bulkInsert(rows) else ZIO.unit } else ZIO.unit } } FEEL IMPERATIVE ACT FUNCTIONALLY RE-WRITING BRANCHING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN!
  • 37. FUNCTIONAL IMPERATIVE FEEL IMPERATIVE ACT FUNCTIONALLY RE-WRITING LOOPING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN! class File { def readLine(): String def close(): Unit } var line: String = file.readLine() while (line != null) { buffer.append(line) line = file.readLine() } class File { def readLine(): ZIO[Any, Throwable, String] def close(): ZIO[Any, Throwable, Unit] } file.readLine().flatMap { line0 => var line = line0 def whileFun(): ZIO[Any, Throwable, Unit] = if (line != null) buffer.append(line) file.readLine().flatMap { lineN => line = lineN whileFun() } else ZIO.unit whileFun()
  • 38. FUNCTIONAL IMPERATIVE FEEL IMPERATIVE ACT FUNCTIONALLY RE-WRITING LOOPING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN! class File { def readLine(): String def close(): Unit } var line: String = file.readLine() while (line != null) { buffer.append(line) line = file.readLine() } class File { def readLine(): ZIO[Any, Throwable, String] def close(): ZIO[Any, Throwable, Unit] } file.readLine().flatMap { line0 => var line = line0 def whileFun(): ZIO[Any, Throwable, Unit] = if (line != null) buffer.append(line) file.readLine().flatMap { lineN => line = lineN whileFun() } else ZIO.unit whileFun() var line: String = file.readLine() while (line != null) { buffer.append(line) line = file.readLine() }
  • 39. FUNCTIONAL IMPERATIVE defer { var line: String = file.readLine().run while (line != null) { buffer.append(line) line = file.readLine().run } } FEEL IMPERATIVE ACT FUNCTIONALLY RE-WRITING LOOPING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN! class File { def readLine(): String def close(): Unit } var line: String = file.readLine() while (line != null) { buffer.append(line) line = file.readLine() } class File { def readLine(): ZIO[Any, Throwable, String] def close(): ZIO[Any, Throwable, Unit] } file.readLine().flatMap { line0 => var line = line0 def whileFun(): ZIO[Any, Throwable, Unit] = if (line != null) buffer.append(line) file.readLine().flatMap { lineN => line = lineN whileFun() } else ZIO.unit whileFun()
  • 40. FUNCTIONAL IMPERATIVE defer { val line0 = file.readLine().run var line: String = line0 while (line != null) { buffer.append(line) val lineN = file.readLine().run line = lineN } } FEEL IMPERATIVE ACT FUNCTIONALLY RE-WRITING LOOPING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN! class File { def readLine(): String def close(): Unit } var line: String = file.readLine() while (line != null) { buffer.append(line) line = file.readLine() } class File { def readLine(): ZIO[Any, Throwable, String] def close(): ZIO[Any, Throwable, Unit] } file.readLine().flatMap { line0 => var line = line0 def whileFun(): ZIO[Any, Throwable, Unit] = if (line != null) buffer.append(line) file.readLine().flatMap { lineN => line = lineN whileFun() } else ZIO.unit whileFun()
  • 41. FUNCTIONAL IMPERATIVE FEEL IMPERATIVE ACT FUNCTIONALLY defer { val line0 = file.readLine().run var line: String = line0 while (line != null) { buffer.append(line) val lineN = file.readLine().run line = lineN } } RE-WRITING LOOPING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN! class File { def readLine(): String def close(): Unit } var line: String = file.readLine() while (line != null) { buffer.append(line) line = file.readLine() } class File { def readLine(): ZIO[Any, Throwable, String] def close(): ZIO[Any, Throwable, Unit] } file.readLine().flatMap { line0 => var line = line0 def whileFun(): ZIO[Any, Throwable, Unit] = if (line != null) buffer.append(line) file.readLine().flatMap { lineN => line = lineN whileFun() } else ZIO.unit whileFun()
  • 42. FUNCTIONAL IMPERATIVE FEEL IMPERATIVE ACT FUNCTIONALLY defer { val line0 = file.readLine().run var line: String = line0 while (line != null) { buffer.append(line) val lineN = file.readLine().run line = lineN } } RE-WRITING LOOPING START WITH IMPERATIVE STYLE... THEN DEFER AND RUN! class File { def readLine(): String def close(): Unit } var line: String = file.readLine() while (line != null) { buffer.append(line) line = file.readLine() } class File { def readLine(): ZIO[Any, Throwable, String] def close(): ZIO[Any, Throwable, Unit] } file.readLine().flatMap { line0 => var line = line0 def whileFun(): ZIO[Any, Throwable, Unit] = if (line != null) buffer.append(line) file.readLine().flatMap { lineN => line = lineN whileFun() } else ZIO.unit whileFun()
  • 43. RE-WRITING ERROR HANDLING EXPECT THE (MILDLY) UNEXPECTED val file = JsonFile.from(path) try { file.readToJson() } catch { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) } finally { file.close() } class JsonFile { def readToJson(): ZIO[Any, Throwable, Json] def close(): ZIO[Any, Throwable, Unit] } object JsonFile { def from(path: String): JsonFile } class JsonFile { def readToJson(): Json def close(): Unit } object JsonFile { def open(path: String): JsonFile } FUNCTIONAL IMPERATIVE succeed(JsonFile.from(path)) .flatMap { file => file.readToJson().catchSome { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) }.ensuring { file.close().orDie }
  • 44. FUNCTIONAL IMPERATIVE val file = JsonFile.from(path) try { file.readToJson() } catch { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) } finally { file.close() } class JsonFile { def readToJson(): ZIO[Any, Throwable, Json] def close(): ZIO[Any, Throwable, Unit] } object JsonFile { def from(path: String): JsonFile } class JsonFile { def readToJson(): Json def close(): Unit } object JsonFile { def open(path: String): JsonFile } val file: JsonFile = JsonFile.from(path) try { file.readToJson() } catch { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) } finally { file.close() } succeed(JsonFile.from(path)) .flatMap { file => file.readToJson().catchSome { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) }.ensuring { file.close().orDie } RE-WRITING ERROR HANDLING EXPECT THE (MILDLY) UNEXPECTED
  • 45. FUNCTIONAL IMPERATIVE defer { val file = JsonFile.from(path) try { file.readToJson().run } catch { case e: IOException => handleIO(e).run case e: DecodingException => handleDE(e).run } finally { file.close().run } } val file = JsonFile.from(path) try { file.readToJson() } catch { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) } finally { file.close() } succeed(JsonFile.from(path)) .flatMap { file => file.readToJson().catchSome { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) }.ensuring { file.close().orDie } class JsonFile { def readToJson(): ZIO[Any, Throwable, Json] def close(): ZIO[Any, Throwable, Unit] } object JsonFile { def from(path: String): JsonFile } class JsonFile { def readToJson(): Json def close(): Unit } object JsonFile { def open(path: String): JsonFile } FEEL IMPERATIVE ACT FUNCTIONALLY RE-WRITING ERROR HANDLING EXPECT THE (MILDLY) UNEXPECTED
  • 46. FUNCTIONAL IMPERATIVE defer { val file = JsonFile.from(path) try { file.readToJson().run } catch { case e: IOException => handleIO(e).run case e: DecodingException => handleDE(e).run } finally { file.close().run } } val file = JsonFile.from(path) try { file.readToJson() } catch { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) } finally { file.close() } succeed(JsonFile.from(path)) .flatMap { file => file.readToJson().catchSome { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) }.ensuring { file.close().orDie } class JsonFile { def readToJson(): ZIO[Any, Throwable, Json] def close(): ZIO[Any, Throwable, Unit] } object JsonFile { def from(path: String): JsonFile } class JsonFile { def readToJson(): Json def close(): Unit } object JsonFile { def open(path: String): JsonFile } FEEL IMPERATIVE ACT FUNCTIONALLY RE-WRITING ERROR HANDLING EXPECT THE (MILDLY) UNEXPECTED
  • 47. file.readLine().flatMap { line0 => var line = line0 def whileFun(): ZIO[Any, Throwable, Unit] = if (line != null) buffer.append(line) file.readLine().flatMap { lineN => line = lineN whileFun() } else ZIO.unit whileFun() db.transactionsEnabled().flatMap { te => if (te) db.lazyFetchEnabled().flatMap { lf => if (lf) db.bulkInsert(rows) else ZIO.unit } else ZIO.unit } Monadless succeed(JsonFile.from(path)).flatMap { jsonFile => jsonFile.readToJson() }.catchSome { case e: IOException => handleIO(e) case e: DecodingException => handleDE(e) }.ensuring { jsonFile.close().orDie } read(fileA).flatMap { textA => read(fileB).flatMap { textB => write(fileC, textA + textB) } } defer { val file = JsonFile.from(path) try { file.readToJson().run } catch { case e: IOException => handleIO(e).run case e: DecodingException => handleDE(e).run } finally { file.close().run } } defer { val db = Database.open().run if ( db.transactionsEnabled().run && db.lazyFetchEnabled().run ) { db.bulkInsert(rows).run } } defer { val textA = read(fileA).run val textB = read(fileB).run write(fileC, textA + textB).run } defer { val line0 = file.readLine().run var line: String = line0 while (line != null) { buffer.append(line) val lineN = file.readLine().run line = lineN } }
  • 48. async { val file = JsonFile.from(path) try { file.readToJson().await } catch { case e: IOException => handleIO(e).await case e: DecodingException => handleDE(e).await } finally { file.close().await } } async { val db = Database.open().await if ( db.transactionsEnabled().await && db.lazyFetchEnabled().await ) { db.bulkInsert(rows).await } } async { val textA = read(fileA).await val textB = read(fileB).await write(fileC, textA + textB).await } async { val line0 = file.readLine().await var line: String = line0 while (line != null) { buffer.append(line) val lineN = file.readLine().await line = lineN } } ISN'T THIS ASYNC-AWAIT ? defer { val p = List(joe, jack, jill).run val a = p.addresses.run (p, a) } defer { val p = query[Person].run val a = query[Address].filter(a.fk == p.id).run (p, a) } Works for Collections! Works for Queries!
  • 49. async { val file = JsonFile.from(path) try { file.readToJson().await } catch { case e: IOException => handleIO(e).await case e: DecodingException => handleDE(e).await } finally { file.close().await } } async { val db = Database.open().await if ( db.transactionsEnabled().await && db.lazyFetchEnabled().await ) { db.bulkInsert(rows).await } } async { val textA = read(fileA).await val textB = read(fileB).await write(fileC, textA + textB).await } async { val line0 = file.readLine().await var line: String = line0 while (line != null) { buffer.append(line) val lineN = file.readLine().await line = lineN } } NO defer { val p = List(joe, jack, jill).run val a = p.addresses.run (p, a) } defer { val p = query[Person].run val a = query[Address].filter(a.fk == p.id).run (p, a) } Works for Collections! Works for Queries!
  • 50. Wouldn't Everyone want this? defer {
  • 51.
  • 52.
  • 53. val line0 = file.readLine().run var line: String = line0 while (line != null) { buffer.append(line) val lineN = file.readLine().run line = lineN } } defer { val textA = run(read(fileA)) val textB = run(read(fileB)) run(write(fileC, textA + textB)) } defer { val db = Database.open().run if ( db.transactionsEnabled().run && db.lazyFetchEnabled().run ) { db.bulkInsert(rows).run } } defer { val file = JsonFile.from(path) try { file.readToJson().run } catch { case e: IOException => handleIO(e).run case e: DecodingException => handleDE(e).run } finally { file.close().run } } SUPPORTS THESE and...
  • 54. defer { val line0 = file.readLine().run var line: String = line0 while (line != null) { buffer.append(line) val lineN = file.readLine().run line = lineN } } defer { val textA = run(read(fileA)) val textB = run(read(fileB)) run(write(fileC, textA + textB)) } defer { val db = Database.open().run if ( db.transactionsEnabled().run && db.lazyFetchEnabled().run ) { db.bulkInsert(rows).run } } defer { val file = JsonFile.from(path) try { file.readToJson().run } catch { case e: IOException => handleIO(e).run case e: DecodingException => handleDE(e).run } finally { file.close().run } } getWebsites() .flatMap { websites => ZIO.foreach(websites)( postUpdate(_) ) } for (site <- getWebsites()) { postUpdate(site) } FOR-LOOP IMPERATIVE TRANSLATION
  • 55. defer { val line0 = file.readLine().run var line: String = line0 while (line != null) { buffer.append(line) val lineN = file.readLine().run line = lineN } } defer { val textA = run(read(fileA)) val textB = run(read(fileB)) run(write(fileC, textA + textB)) } TRANSLATION defer { val db = Database.open().run if ( db.transactionsEnabled().run && db.lazyFetchEnabled().run ) { db.bulkInsert(rows).run } } defer { val file = JsonFile.from(path) try { file.readToJson().run } catch { case e: IOException => handleIO(e).run case e: DecodingException => handleDE(e).run } finally { file.close().run } } getWebsites() .flatMap { websites => ZIO.foreach(websites)( postUpdate(_) ) } IMPERATIVE FOR defer { for (site <- getWebsites().run) { postUpdate(site).run } }
  • 56. defer { val line0 = file.readLine().run var line: String = line0 while (line != null) { buffer.append(line) val lineN = file.readLine().run line = lineN } } defer { val textA = run(read(fileA)) val textB = run(read(fileB)) run(write(fileC, textA + textB)) } TRANSLATION defer { val db = Database.open().run if ( db.transactionsEnabled().run && db.lazyFetchEnabled().run ) { db.bulkInsert(rows).run } } defer { val file = JsonFile.from(path) try { file.readToJson().run } catch { case e: IOException => handleIO(e).run case e: DecodingException => handleDE(e).run } finally { file.close().run } } getWebsites() .flatMap { websites => ZIO.foreachPar(websites)( postUpdate(_) ) } IMPERATIVE FOR defer(Params(Collect.Parallel)) { for (site <- getWebsites().run) { postUpdate(site).run } }
  • 58. val out: ZIO[CustomerConfig & DistributorConfig, Throwable, (Customer, Distributor)] = defer { val custUrl: String = ZIO.service[CustomerConfig].run.url val distUrl: String = ZIO.service[DistributorConfig].run.url (parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run)) } ZIO-DIRECT Direct Programming tailored to ZIO def httpGet(url: String): ZIO[Any, Throwable, String] def parseCustomer(customer: String): Customer def parseDistrubutor(customer: String): Distributor case class CustomerConfig(url: String) case class DistributorConfig(url: String)
  • 59. val out: ZIO[CustomerConfig & DistributorConfig, Throwable, (Customer, Distributor)] = defer { val custUrl: String = ZIO.service[CustomerConfig].run.url val distUrl: String = ZIO.service[DistributorConfig].run.url (parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run)) } ZIO-DIRECT Environment? Composes! API def httpGet(url: String): ZIO[Any, Throwable, String] def parseCustomer(customer: String): Customer def parseDistrubutor(customer: String): Distributor case class CustomerConfig(url: String) case class DistributorConfig(url: String)
  • 60. val out: ZIO[CustomerConfig & DistributorConfig, IOException, (Customer, Distributor)] = defer { val custUrl: String = ZIO.service[CustomerConfig].run.url val distUrl: String = ZIO.service[DistributorConfig].run.url (parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run)) } def httpGet(url: String): ZIO[Any, IOException, String] def parseCustomer(customer: String): Customer def parseDistrubutor(customer: String): Distributor case class CustomerConfig(url: String) case class DistributorConfig(url: String) ZIO-DIRECT Errors? Compose! API
  • 61. val out: ZIO[..., CustomerGetException | DistributorGetException, (...)] = defer { val custUrl: String = ZIO.service[CustomerConfig].run.url val distUrl: String = ZIO.service[DistributorConfig].run.url (parseCustomer(httpGetCustomer(custUrl).run), parseDistrubutor(httpGetDistributor(distUrl).run)) } def httpGetCustomer(url: String): ZIO[Any, CustomerGetException, String] def httpGetDistributor(url: String): ZIO[Any, DistrubutorGetException, String] def parseCustomer(customer: String): Customer def parseDistrubutor(customer: String): Distributor case class CustomerConfig(url: String) case class DistributorConfig(url: String) API ZIO-DIRECT Errors? Compose!
  • 62. val out: ZIO[..., Exception, (...)] = defer(Params(TypeUnion.LeastUpper)) { val custUrl: String = ZIO.service[CustomerConfig].run.url val distUrl: String = ZIO.service[DistributorConfig].run.url (parseCustomer(httpGetCustomer(custUrl).run), parseDistrubutor(httpGetDistributor(distUrl).run)) } def httpGetCustomer(url: String): ZIO[Any, CustomerGetException, String] def httpGetDistributor(url: String): ZIO[Any, DistrubutorGetException, String] def parseCustomer(customer: String): Customer def parseDistrubutor(customer: String): Distributor case class CustomerConfig(url: String) case class DistributorConfig(url: String) API ZIO-DIRECT Errors? Compose... in Two Ways!
  • 63. ZIO-DIRECT Want to see it up close?
  • 64. ZIO-DIRECT Use defer.info root> compile ... [info] -- Info: /Users/me/zio-direct/src/test/scala-3.x/zio/direct/examples/TailoredForZio.scala:40:8 [info] 40 | val out = [info] | ^ [info] |Computed Type: ZIO[CustomerConfig & DistributorConfig, CustomerGetException | DistrubutorGetException, Tuple2[Customer, Distributor]]
  • 65. ZIO-DIRECT root> compile ... [info] -- Info: /Users/me/zio-direct/src/test/scala-3.x/zio/direct/examples/TailoredForZio.scala:40:8 [info] 40 | val out = [info] | ^ [info] |Computed Type: ZIO[CustomerConfig & DistributorConfig, CustomerGetException | DistrubutorGetException, Tuple2[Customer, Distributor]] Use defer.info
  • 66. ZIO-DIRECT Use defer.info or defer.verbose root> compile [info] -- Info: /Users/me/zio-direct/src/test/scala-3.x/zio/direct/examples/TailoredForZio.scala:40:8 [info] 40 | val out = [info] | ^ [info] |Computed Type: ZIO[CustomerConfig & DistributorConfig, CustomerGetException | DistrubutorGetException, Tuple2[Customer, Distributor]]
  • 67. ZIO-DIRECT Use defer.info or defer.verbose root> compile [info] -- Info: /Users/me/zio-direct/src/test/scala-3.x/zio/direct/examples/TailoredForZio.scala:40:8 [info] 40 | val out = [info] | ^ [info] |Computed Type: ZIO[CustomerConfig & DistributorConfig, CustomerGetException | DistrubutorGetException, Tuple2[Customer, Distributor]] ZIO.service[CustomerConfig].map { (sm: CustomerConfig) => sm.url }.flatMap { custUrl => ... }
  • 68. ZIO-DIRECT Use defer.info or defer.verbose root> compile [info] -- Info: /Users/me/zio-direct/src/test/scala-3.x/zio/direct/examples/TailoredForZio.scala:40:8 [info] 40 | val out = [info] | ^ [info] |Computed Type: ZIO[CustomerConfig & DistributorConfig, CustomerGetException | DistrubutorGetException, Tuple2[Customer, Distributor]] ZIO.service[CustomerConfig].map { (sm: CustomerConfig) => val runVal: CustomerConfig = sm sm.url }.flatMap { (v: String) => val custUrl: String = v }
  • 69. ZIO-DIRECT Use defer.info or defer.verbose root> compile === Reconstituted Code ======== ZIO.service[CustomerConfig].map(((sm: CustomerConfig) => { val runVal: CustomerConfig = sm runVal.url })).asInstanceOf[ZIO[_, _, String]].flatMap(((v: String) => { val custUrl: String = v ZIO.service[DistributorConfig].map(((`sm₂`: DistributorConfig) => { val `runVal₂`: DistributorConfig = `sm₂` `runVal₂`.url })).asInstanceOf[ZIO[_, _, String]].flatMap(((`v₂`: String) => { val distUrl: String = `v₂` ZIO.collectAll(Chunk.from(List.apply(httpGetCustomer(custUrl), httpGetDistributor(distUrl)))).map(((terms: Chunk[Any]) => { val iter: Iterator[Any] = terms.iterator { val `runVal₃`: String = iter.next.asInstanceOf[String] val `runVal₄`: String = iter.next.asInstanceOf[String] Tuple2.apply(parseCustomer(`runVal₃`), parseDistrubutor(`runVal₄`)) }.asInstanceOf[Tuple2[Customer, Distributor]] })).asInstanceOf[ZIO[_, _, Tuple2[Customer, Distributor]]] })) }))
  • 71. val out = defer { val custUrl: String = ZIO.service[CustomerConfig].run.url val distUrl: String = ZIO.service[DistributorConfig].run.url (parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run)) } ZIO-DIRECT Wait... how do you do that? That's an arbitrary construct... how do you do that?
  • 72. val out = defer { val custUrl: String = ZIO.service[CustomerConfig].run.url val distUrl: String = ZIO.service[DistributorConfig].run.url (parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run)) } ZIO-DIRECT Mystery Challenge... spot the IOs! ZIO.service[CustomerConfig].flatMap { cc => val custUrl: String = cc.url ZIO.service[DistributorConfig].flatMap { dc => val distUrl: String = dc.url ZIO.collectAll(Chunk(httpGet(custUrl), (httpGet(distUrl)))).map { col => val iter = col.iterator (parseCustomer(iter.next()), parseDistrubutor(iter.next())) } } }
  • 73. val out = defer { val custUrl: String = ZIO.service[CustomerConfig].run.url val distUrl: String = ZIO.service[DistributorConfig].run.url (parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run)) } ZIO-DIRECT Pull them out of the line body... ZIO.service[CustomerConfig].flatMap { cc => val custUrl: String = cc.url ZIO.service[DistributorConfig].flatMap { dc => val distUrl: String = dc.url ZIO.collectAll(Chunk(httpGet(custUrl), (httpGet(distUrl)))).map { col => val iter = col.iterator (parseCustomer(iter.next()), parseDistrubutor(iter.next())) } } }
  • 74. val out = defer { val custUrl: String = ZIO.service[CustomerConfig].run.url val distUrl: String = ZIO.service[DistributorConfig].run.url (parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run)) } ZIO-DIRECT Collect, iterate, and splice back in! ZIO.service[CustomerConfig].flatMap { cc => val custUrl: String = cc.url ZIO.service[DistributorConfig].flatMap { dc => val distUrl: String = dc.url ZIO.collectAll(Chunk(httpGet(custUrl), (httpGet(distUrl)))).map { col => val iter = col.iterator (parseCustomer(iter.next()), parseDistrubutor(iter.next())) } } }
  • 75. ZIO-DIRECT If you have no mutable data structures and no lazy actions... things in here cannot interact between each other... therefore, I can extract them! (parseCustomer(httpGet(custUrl).run), parseDistrubutor(httpGet(distUrl).run))
  • 76. ZIO-DIRECT Things in defer blocks must be: EAGER + IMMUTABLE (unless they are wrapped in an effect) CORRECTENESS {x ⟺ y}
  • 77. yes... it's very important! CORRECTNESS defer(Params(Verify.None)) { val db = Database.open.run while (db.hasNextRow()) { if (db.lockNextRow()) doSomethingWith(db.nextRow().run) else wait() } } class Database { def nextRow(): ZIO[Any, Throwable, Row] def hasNextRow(): Boolean def lockNextRow(): Boolean //somehow not an effect } API
  • 78. yes... it's very important! CORRECTNESS defer(Params(Verify.None)) { val db = Database.open.run while (db.hasNextRow()) { if (db.lockNextRow()) doSomethingWith(db.nextRow().run) else wait() } } class Database { def nextRow(): ZIO[Any, Throwable, Row] def hasNextRow(): Boolean def lockNextRow(): Boolean //somehow not an effect } API Database.open.flatMap { db => def whileFun(): ZIO[Any, Throwable, Unit] = if (db.hasNextRow()) db.nextRow().flatMap { row => // Too late to check if row is locked, we already READ IT!! if (db.lockNextRow()) doSomethingWith(row) else waitT() whileFun() } else ZIO.unit whileFun() }
  • 79. yes... it's very important! CORRECTNESS defer(Params(Verify.None)) { val db = Database.open.run while (db.hasNextRow()) { if (db.lockNextRow()) doSomethingWith(db.nextRow().run) else wait() } } class Database { def nextRow(): ZIO[Any, Throwable, Row] def hasNextRow(): Boolean def lockNextRow(): Boolean //somehow not an effect } API === Reconstituted Code ======== FunctionalObjectModel.Database.open.flatMap(((v: Database) => { val db: Database = v def whileFunc: ZIO[Any, Throwable, Unit] = if (db.hasNextRow) if (db.lockNextRow) db.nextRow.map(((sm: Row) => { val runVal: Row = sm FunctionalObjectModel.doSomethingWith(runVal) })) else zio.ZIO.succeed(FunctionalObjectModel.waitT).flatMap(((`v₂`: Unit) => whileFunc)) else zio.ZIO.succeed(()) whileFunc }))
  • 80. yes... it's very important! CORRECTNESS defer(Params(Verify.None)) { val db = Database.open.run while (ZIO.succeed(db.hasNextRow()).run) { if (ZIO.succeed(db.lockNextRow()).run) doSomethingWith(db.nextRow().run) else wait() } } class Database { def nextRow(): ZIO[Any, Throwable, Row] def hasNextRow(): Boolean def lockNextRow(): Boolean //somehow not an effect } API
  • 81. Here's the thing you shouldn't do! CORRECTNESS defer(Params(Verify.None)) { val db = Database.open.run (db.lockNextRow(), db.nextRow().run) === Reconstituted Code ======== FunctionalObjectModel.Database.open.flatMap(((v: Database) => { val db: Database = v db.nextRow.map(((sm: Row) => { val runVal: Row = sm Tuple2.apply(db.lockNextRow, runVal) })) }))
  • 82. CORRECTNESS val a = effectA.run <expressions-using a> val b = effectB.run <expressions-using a, b> val c = effectC.run <expressions-using a, b, c> effectA.flatMap { a => <expressions-using a> effectB.flatMap { b => <expressions-using a, b> effectC.flatMap { <expressions-using a, b, c> } } } The Basic Intuition
  • 83. CORRECTNESS val a = effectA.run <expressions-using a> val b = effectB.run <expressions-using a, b> val c = effectC.run <expressions-using a, b, c> effectA.flatMap { a => <expressions-using a> effectB.flatMap { b => <expressions-using a, b> effectC.flatMap { <expressions-using a, b, c> } } } val a = effectA.run <expressions-using a> val b = effectB.run <expressions-using a, b> <expressions-using effectC.run, effectD.run> effectA.flatMap { a => <expressions-using a> effectB.flatMap { b => <expressions-using a, b> foreach(effectC, effectD) { list => <expressions-using list.get(0), list.get(1)> } } } The Basic Intuition
  • 84. CORRECTNESS val a = effectA.run <expressions-using a> val b = effectB.run <expressions-using a, b> val c = effectC.run <expressions-using a, b, c> val a = effectA.run <expressions-using a> val b = effectB.run <expressions-using a, b> <expressions-using effectC.run, effectD.run> effectA.flatMap { a => <expressions-using a> effectB.flatMap { b => <expressions-using a, b> foreach(effectC, effectD) { list => <expressions-using list.get(0), list.get(1)> } } } The Basic Intuition NO INTER-LINE INTERACTIONS NO INTER-LINE INTERACTIONS effectA.flatMap { a => <expressions-using a> effectB.flatMap { b => <expressions-using a, b> effectC.flatMap { c => <expressions-using a, b, c> } } }
  • 85. CORRECTNESS val a = effectA.run <expressions-using a> val b = effectB.run <expressions-using a, b> val c = effectC.run <expressions-using a, b, c> effectA.flatMap { a => <expressions-using a> effectB.flatMap { b => <expressions-using a, b> effectC.flatMap { c => <expressions-using a, b, c> } } } val a = effectA.run <expressions-using a> val b = effectB.run <expressions-using a, b> <expressions-using effectC.run, effectD.run> effectA.flatMap { a => <expressions-using a> effectB.flatMap { b => <expressions-using a, b> foreach(effectC, effectD) { list => <expressions-using list.get(0), list.get(1)> } } } The Basic Intuition ALL INTERACTIONS HAPPEN DOWNWARD ALL INTERACTIONS HAPPEN DOWNWARD
  • 86. CORRECTNESS effectA.flatMap { a => <expressions-using a> effectB.flatMap { b => <expressions-using a, b> effectC.flatMap { c => <expressions-using a, b, c> } } } effectA.flatMap { a => <expressions-using a> effectB.flatMap { b => <expressions-using a, b> ZIO.foreach(effectC, effectD) { list => <expressions-using list.get(0), list.get(1)> } } } The Basic Intuition ALL INTERACTIONS HAPPEN DOWNWARD ALL INTERACTIONS HAPPEN DOWNWARD SEQUENTIALITY =:= NESTING
  • 87. CORRECTNESS effectA.flatMap { a => <expressions-using a> effectB.flatMap { b => <expressions-using a, b> effectC.flatMap { c => <expressions-using a, b, c> } } } effectA.flatMap { a => <expressions-using a> effectB.flatMap { b => <expressions-using a, b> foreach(effectC, effectD) { list => <expressions-using list.get(0), list.get(1)> } } } The Basic Intuition ALL INTERACTIONS HAPPEN DOWNWARD ALL INTERACTIONS HAPPEN DOWNWARD for { a <- effectA.run _ = <expressions-using a> b <- effectB.run _ = <expressions-using a, b> c <- effectC.run } yield <expressions-using a, b, c>
  • 88. CORRECTNESS The Re-Write Rules - If the statement already transformed e.g. defer { defer { ... } }: - Chain it in a fl atMap and continue - If statement is a try: - Recurse on the body, then the catch clauses, then the fi nalizer if it exists. - Chain all of them in nested fl atMaps (or maps if they have no run statements inside) - If statement is a throw, or unsafe { ... } block: - Chain it in a fl atMap (or map if it has run statements inside) - If statement is an if, for-loop, or match statement - Recurse on the pre fi x/condition and then the body statement(s). - Chain both in fl atMaps (or maps if they have no run statements inside) - If statement is a run call: - Chain it in a fl atMap - If statement is a block e.g. { a; b; c } - Chain each clause in a fl atMap (or maps if it has no `run` statements inside) - Otherwise if the statement has no vars, defs, classes mutable state, or any other forbidden construct: - Extract each run call into a ZIO. collect - Then splice in the iteration steps in each call-site. - Chain the whole thing in a fl atMap
  • 89. CORRECTNESS Other Forbidden Things! • lazy value declarations • class declarations • function declarations • lambdas/closures • implicit variables/functions OTHER THINGS FORBIDDEN IN DEFER-CLAUSES (Unless they are in the .run calls)
  • 90. Constraints Certainty Capability CORRECTNESS NO lazy value declarations NO class declarations NO function declarations NO lambdas/closures NO implicit variables/functions Cut-Points Work Splicing Correct Sequencing Correct Generated Code Correct Direct-Style Syntax Language-Level Constructs
  • 91. CORRECTNESS Mutation... Forbidden! var x = 1 defer.info { ({ x = x + 1; x }, ZIO.succeed(x).run) } > runMain (2, 1) ...but why not (2, 2) ???
  • 92. CORRECTNESS Mutable Collections... Forbidden! > runMain (2, 1) defer.info { ({ buff.update(0, buff(0) + 1); buff(0) }, ZIO.succeed(buff(0)).run) }
  • 93. CORRECTNESS Mutable Collections... Forbidden! Unless inside run(...) clauses Things in defer blocks must be: EAGER + IMMUTABLE (unless they are wrapped in an effect!!! ...and called with .run) CORRECTENESS
  • 94. ARGUMENT 1 It is not Referentially Transparent
  • 95. ARGUMENT 1 It is not Referentially Transparent Definition: An expression shall called referentially transparent if it can be replaced with its corresponding value without changing the program's behavior. Interpretation: Ordering of value-terms should not matter.
  • 96. ARGUMENT 1 It is not Referentially Transparent defer { val int = run(List(1, 2)) val bool = run(List(true, false)) (int, bool) } // List((1, true), (1, false), (2, true), (2, false)) defer { val bool = run(List(true, false)) val int = run(List(1, 2)) (int, bool) } // List((1, true), (2, true), (1, false), (2, false))
  • 97. ARGUMENT 1 It is not Referentially Transparent defer.info { val int = run(List(1, 2)) val bool = run(List(true, false)) (int, bool) } // List((1, true), (1, false), (2, true), (2, false)) defer.info { val bool = run(List(true, false)) val int = run(List(1, 2)) (int, bool) } // List((1, true), (2, true), (1, false), (2, false)) List(1, 2).flatMap { int => List(true, false).map { bool => (int, bool) } } List(true, false).flatMap { int => List(1, 2).map { bool => (int, bool) } }
  • 98. ARGUMENT 1 It is not Referentially Transparent List(1, 2).flatMap { int => List(true, false).map { bool => (int, bool) } } List(true, false).flatMap { int => List(1, 2).map { bool => (int, bool) } } ... but the things on the left hand side are PURE VALUES since they have no monad signature! defer.info { val int = run(List(1, 2)) val bool = run(List(true, false)) (int, bool) } // List((1, true), (1, false), (2, true), (2, false)) defer.info { val bool = run(List(true, false)) val int = run(List(1, 2)) (int, bool) } // List((1, true), (2, true), (1, false), (2, false))
  • 99. ARGUMENT 1 It is not Referentially Transparent defer { val int: Int = run(List(1, 2)) val bool: Boolean = run(List(true, false)) (int, bool) } // List((1, true), (1, false), (2, true), (2, false)) defer { val bool: Boolean = run(List(true, false)) val int: Int = run(List(1, 2)) (int, bool) } // List((1, true), (2, true), (1, false), (2, false)) List(1, 2).flatMap { int => List(true, false).map { bool => (int, bool) } } List(true, false).flatMap { int => List(1, 2).map { bool => (int, bool) } } ... but the things on the left hand side are PURE VALUES since they have no monad signature! No, they're just syntactic sugar for flatMap/map variables. Same as the left-hand of a for comprehension. Look...
  • 100. ARGUMENT 1 It is not Referentially Transparent List(1, 2).flatMap { int => List(true, false).map { bool => (int, bool) } } List(true, false).flatMap { int => List(1, 2).map { bool => (int, bool) } } for { int: Int <- List(1, 2) bool: Boolean <- List(true, false) } yield (int, bool) for { bool: Boolean <- List(true, false) int: Int <- List(1, 2) } yield (int, bool) defer { val int: Int = run(List(1, 2)) val bool: Boolean = run(List(true, false)) (int, bool) } // List((1, true), (1, false), (2, true), (2, false)) defer { val bool: Boolean = run(List(true, false)) val int: Int = run(List(1, 2)) (int, bool) } // List((1, true), (2, true), (1, false), (2, false))
  • 101. ARGUMENT 1 It is not Referentially Transparent List(1, 2).flatMap { int => List(true, false).map { bool => (int, bool) } } List(true, false).flatMap { int => List(1, 2).map { bool => (int, bool) } } No Equivalent for { bool: Boolean <- List(true, false) int: Int <- List(1, 2) } yield (int, bool) defer { val int: Int = run(List(1, 2)) (int, run(List(true, false))) } // List((1, true), (1, false), (2, true), (2, false)) defer { val bool: Boolean = run(List(true, false)) val int: Int = run(List(1, 2)) (int, bool) } // List((1, true), (2, true), (1, false), (2, false))
  • 102. ARGUMENT 1 defer { val int = List(1, 2).run val bool = List(true, false).run val bool1 = bool val int1 = int (int, bool) } // List((1, true), (1, false), (2, true), (2, false)) defer { val bool = List(true, false).run val int = List(1, 2).run val bool1 = bool val int1 = int (int, bool) } // List((1, true), (2, true), (1, false), (2, false)) It is not Referentially Transparent... Take 613 List(1, 2).flatMap { int => List(true, false).flatMap { bool => val bool1 = bool val int1 = int (int, bool) } } List(true, false).flatMap { int => List(1, 2).flatMap { bool => val bool1 = bool val int1 = int (int, bool) } }
  • 103. defer { val int = List(1, 2) val bool = List(true, false) val bool1 = bool.run val int1 = int.run (int, bool) } // List((1, true), (2, true), (1, false), (2, false)) defer { val bool = List(true, false) val int = List(1, 2) val bool1 = bool.run val int1 = int.run (int, bool) } // List((1, true), (2, true), (1, false), (2, false)) ARGUMENT 1 val int = List(1, 2) val bool = List(true, false) bool.flatMap { bool => int.map { int => (int, bool) } } val bool = List(true, false) val int = List(1, 2) bool.flatMap { bool => int.map { int => (int, bool) } } It is not Referentially Transparent... Take 613
  • 104. ARGUMENT 2 It is Lawless
  • 105. ARGUMENT 2 It is Lawless I. Ye shall not use mutable things inside of a defer unless the statement is wrapped in a run(...) or UNSAFE(...) block. II. Ye shall not use lazy things inside of a defer unless the statement is wrapped in a run(...) or UNSAFE(...) block. III. Ye shall not interact with external state inside of a defer unless the statement is wrapped in a run(...) or unsafe(...) block. IV. All run(...) statements inside a defer shall be run from top to bottom if they are in different lines, and from left to right if they are on the same line. V. All plain statements inside of a defer shall be run after the run(...) on the line above and before the run(...) on the line below. VI. In the case that there are plain statements and run(ZIO)s on the same line, the run(ZIO)s can be executed before the plain statements in some circumstances. To ensure correctness in this scenario, make sure to follow laws I, II, and III.
  • 106. Because ZIO actually has them.... ERROR CHANNELS
  • 107. ZIO.fail(new Boom()) ZIO.succeed(pretendPureCode()) ERROR CHANNELS 💣 To Error Channel 💣 To Defect Channel class Boom extends Exception("Boom!") zio.direct.BoomExamples$Boom: Boom! at zio.direct.BoomExamples$.main$$anonfun$1(BoomExamples.scala:22) at zio.ZIO$.fail$$anonfun$1(ZIO.scala:3021) at zio.ZIO$.failCause$$anonfun$1(ZIO.scala:3027) at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:986) zio.direct.BoomExamples$Boom: Boom! at zio.direct.BoomExamples$.main$$anonfun$1(BoomExamples.scala:22) at zio.UnsafeVersionSpecific.implicitFunctionIsFunction$$anonfun$1(UnsafeVersionSpecific.scala:23) at zio.Unsafe$.unsafe(Unsafe.scala:37) at zio.ZIOCompanionVersionSpecific.succeed$$anonfun$1(ZIOCompanionVersionSpecific.scala:161) at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:831) ZIO.succeed(pureCode) < pureCode > No Error def pretendPureCode() = throw new Boom() Expecting the somewhat/somewhat-not unexpected
  • 108. ZIO.fail(new Boom()) ZIO.succeed(pretendPureCode()) ERROR CHANNELS ZIO.attempt(pretendPureCode()) 💣 To Error Channel 💣 To Defect Channel 💣 To Error Channel class Boom extends Exception("Boom!") zio.direct.BoomExamples$Boom: Boom! at zio.direct.BoomExamples$.main$$anonfun$1(BoomExamples.scala:22) at zio.ZIO$.fail$$anonfun$1(ZIO.scala:3021) at zio.ZIO$.failCause$$anonfun$1(ZIO.scala:3027) at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:986) zio.direct.BoomExamples$Boom: Boom! at zio.direct.BoomExamples$.main$$anonfun$1(BoomExamples.scala:22) at zio.UnsafeVersionSpecific.implicitFunctionIsFunction$$anonfun$1(UnsafeVersionSpecific.scala:23) at zio.Unsafe$.unsafe(Unsafe.scala:37) at zio.ZIOCompanionVersionSpecific.succeed$$anonfun$1(ZIOCompanionVersionSpecific.scala:161) at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:831) zio.direct.BoomExamples$Boom: Boom! at zio.direct.BoomExamples$.main$$anonfun$1(BoomExamples.scala:22) at zio.ZIOCompanionVersionSpecific.attempt$$anonfun$1(ZIOCompanionVersionSpecific.scala:107) at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:967) ZIO.succeed(pureCode) < pureCode > No Error def pretendPureCode() = throw new Boom() Expecting Attempting the somewhat/somewhat-not unexpected
  • 109. ZIO.fail(new Boom()) ZIO.succeed(pretendPureCode()) ERROR CHANNELS ZIO.attempt(pretendPureCode()) 💣 To Error Channel 💣 To Defect Channel 💣 To Error Channel class Boom extends Exception("Boom!") ZIO.succeed(pureCode) No Error def pretendPureCode() = throw new Boom() defer { pureCode } defer { throw new Boom() } ??? defer { pretendPureCode() } Expecting Attempting the somewhat/somewhat-not unexpected
  • 110. ZIO.fail(new Boom()) ZIO.succeed(pretendPureCode()) ERROR CHANNELS ZIO.attempt(pretendPureCode()) 💣 To Error Channel 💣 To Defect Channel 💣 To Error Channel class Boom extends Exception("Boom!") ZIO.succeed(pureCode) No Error def pretendPureCode() = throw new Boom() defer { pureCode } defer { throw new Boom() } defer { pretendPureCode() } defer { ZIO.attempt(pretendPureCode()).run }
  • 111. ZIO.fail(new Boom()) ZIO.succeed(pretendPureCode()) ERROR CHANNELS ZIO.attempt(pretendPureCode()) 💣 To Error Channel 💣 To Defect Channel 💣 To Error Channel class Boom extends Exception("Boom!") ZIO.succeed(pureCode) No Error def pretendPureCode() = throw new Boom() defer { pureCode } defer { throw new Boom() } defer { pretendPureCode() } defer { unsafe(pretendPureCode()) }
  • 112. ERROR CHANNELS object Database { def openConnection(): ZIO[Scope, Throwable, Connection] } object S3Object { def openInputStream(path: String): ZIO[Scope, Throwable, InputStream] } defer { try { val input = S3Object.openInputStream("foo/bar").run val reader = InputStreamReader(input) val conn = Database.openConnection().run val ps = conn.prepareStatement("INSERT ? INTO Someplace") ps.setClob(1, reader) ps.execute() } catch { case e: IOException => handle(e).run case e: SQLException => handle(e).run } } A "Real Cloud World" Example
  • 113. ERROR CHANNELS object Database { def openConnection(): ZIO[Scope, Throwable, Connection] } object S3Object { def openInputStream(path: String): ZIO[Scope, Throwable, InputStream] } defer { try { val input = S3Object.openInputStream("foo/bar").run val reader = InputStreamReader(input) val conn = Database.openConnection().run val ps = conn.prepareStatement("INSERT ? INTO Someplace") ps.setClob(1, reader) ps.execute() } catch { case e: IOException => handle(e).run case e: SQLException => handle(e).run } } Could Potentially Throw Something! A "Real Cloud World" Example
  • 114. ERROR CHANNELS object Database { def openConnection(): ZIO[Scope, Throwable, Connection] } object S3Object { def openInputStream(path: String): ZIO[Scope, Throwable, InputStream] } defer { try { val input = S3Object.openInputStream("foo/bar").run val reader = unsafe(InputStreamReader(input)) val conn = Database.openConnection().run val ps = unsafe(conn.prepareStatement("INSERT ? INTO Someplace")) unsafe { ps.setClob(1, reader) ps.execute() } } catch { case e: IOException => handle(e).run case e: SQLException => handle(e).run } } A "Real Cloud World" Example
  • 115. ERROR CHANNELS object Database { def openConnection(): ZIO[Scope, Throwable, Connection] } object S3Object { def openInputStream(path: String): ZIO[Scope, Throwable, InputStream] } defer { try { val input = S3Object.openInputStream("foo/bar").run val reader = unsafe(InputStreamReader(input)) val conn = Database.openConnection().run val ps = unsafe(conn.prepareStatement("INSERT ? INTO Someplace")) unsafe { ps.setClob(1, reader) ps.execute() } } catch { case e: IOException => handle(e).run case e: SQLException => handle(e).run } } A "Real Cloud World" Example val out: ZIO[Scope, Throwable, Unit] =
  • 116. ERROR CHANNELS object Database { def openConnection(): ZIO[Scope, Throwable, Connection] } object S3Object { def openInputStream(path: String): ZIO[Scope, Throwable, InputStream] } defer { try { unsafe { val input = S3Object.openInputStream("foo/bar").run val reader = InputStreamReader(input) val conn = Database.openConnection().run val ps = conn.prepareStatement("INSERT ? INTO Someplace") ps.setClob(1, reader) ps.execute() } } catch { case e: IOException => handle(e).run case e: SQLException => handle(e).run } } A "Real Cloud World" Example Or wrap the entire thing!
  • 117. ERROR CHANNELS defer { try { unsafe { val input = S3Object.openInputStream("foo/bar").run val reader = InputStreamReader(input) val conn = Database.openConnection().run val ps = conn.prepareStatement("INSERT ? INTO Someplace") ps.setClob(1, reader) ps.execute() } } catch { case e: IOException => handle(e).run case e: SQLException => handle(e).run } } A "Real Cloud World" Example === Reconstituted Code ======== S3Object.openInputStream("foo/bar").flatMap(((v: InputStream) => { val input: InputStream = v val reader: InputStreamReader = new InputStreamReader(input) Database.openConnection.map(((`v₂`: Connection) => { val conn: Connection = `v₂` val ps: PreparedStatement = conn.prepareStatement("INSERT ? INTO Someplace") ps.setClob(1, reader) ps.execute })) })).catchSome(((tryLamParam: Throwable) => tryLamParam match { case e: IOException => handle(e) case e: SQLException => handle(`e₂`) })) === Reconstituted Code ======== ObjectModel.S3Object.openInputStream("foo/bar").flatMap(((v: InputStream) => { val input: InputStream = v ZIO.attempt(new InputStreamReader(input)).flatMap(((`v₂`: InputStreamReader) => { val reader: InputStreamReader = `v₂` ObjectModel.Database.openConnection.flatMap(((`v₃`: Connection) => { val conn: Connection = `v₃` ZIO.attempt(conn.prepareStatement("INSERT ? INTO Someplace")).flatMap(((`v₄`: PreparedStatement) => { val ps: PreparedStatement = `v₄` ZIO.attempt({ ps.setClob(1, reader) ps.execute }) })) })) })) })).catchSome(((tryLamParam: Throwable) => tryLamParam match { case e: IOException => ObjectModel.handle(e) case e: SQLException => ObjectModel.handle(`e₂`) })) Wrap in Unsafe Generated Code wraps ZIO.attempt
  • 118. USING REFS var i: Int = 10 while (i > 0) { println(s"Currently: ${i}") i = i - 1 } Looping with vars Looping with Refs Okay... ?
  • 119. USING REFS var i: Int = 10 while (i > 0) { println(s"Currently: ${i}") i = i - 1 } Looping with vars Looping with Refs val i: UIO[Ref[Int]] = Ref.make(10) i.flatMap { i0 => def whileLoop(): ZIO[Any, Nothing, Unit] = i0.get.flatMap { iv => if (iv > 0) { println(s"Currently: ${iv}") i0.update(i => i - 1).flatMap { _ => whileLoop() } } else { ZIO.unit } } whileLoop() } Let's pull some teeth!
  • 120. USING REFS var i: Int = 10 while (i > 0) { println(s"Currently: ${i}") i = i - 1 } Looping with vars Looping with Refs val i: UIO[Ref[Int]] = Ref.make(10) i.flatMap { i0 => def whileLoop(): ZIO[Any, Nothing, Unit] = i0.get.flatMap { iv => if (iv > 0) { println(s"Currently: ${iv}") i0.update(i => i - 1).flatMap { _ => whileLoop() } } else { ZIO.unit } } whileLoop() } Let's pull some teeth! Be sure these are right... or it runs forever!
  • 121. USING REFS var i: Int = 10 while (i > 0) { println(s"Currently: ${i}") i = i - 1 } Looping with vars Looping with Refs - Take 2 defer { val i: Ref[Int] = Ref.make(10).run while (i.get.run > 0) { println(s"Currently: ${i.get.run}") i.update(i => i - 1).run } }
  • 122. USING REFS var i: Int = 10 while (i > 0) { println(s"Currently: ${i}") i = i - 1 } Looping with vars Looping with Refs - Take 2 defer.info { val i: Ref[Int] = Ref.make(10).run while (i.get.run > 0) { println(s"Currently: ${i.get.run}") i.update(i => i - 1).run } } === Reconstituted Code ======== Ref.make(10).flatMap(((v: Ref[Int]) => { val i: Ref[Int] = v def whileFunc: ZIO[Any, Nothing, Unit] = i.get.map(((sm: Int) => { val runVal: Int = sm runVal.>(0) })).flatMap(((`v₂`: Boolean) => { val ifVar: Boolean = `v₂` if (ifVar) i.get.map(((`sm₂`: Int) => { val `runVal₂`: Int = `sm₂` println(_root_.scala.StringContext.apply("Currently: ", "").s(`runVal₂`)) })).flatMap(((`v₃`: Unit) => i.update(((`i₂`: Int) => `i₂`.-(1))))).flatMap(((`v₄`: Any) => whileFunc)) else zio.ZIO.succeed(()) })) whileFunc }))
  • 123. USING REFS Quicksort with Mutable Indices def quicksortImperative(a: Array[Int]): Unit = { def swap(i: Int, j: Int): Unit = { val t = a(i) a(i) = a(j) a(j) = t } def sort(l: Int, r: Int): Unit = { val pivot = a((l + r) / 2) var i = l var j = r while (i <= j) { while (a(i) < pivot) i += 1 while (a(j) > pivot) j -= 1 if (i <= j) { swap(i, j) i += 1 j -= 1 } } if (l < j) sort(l, j) if (j < r) sort(i, r) } sort(0, a.length - 1) } val arr = Array(3, 2, 8, 5, 7, 2, 3, 8, 9, 4, 5, 8, 2, 3, 4, 7, 6, 5, 9, 2, 3, 8, 4, 7, 5, 6, 2, 0, 8, 3) quicksortImperative(arr) println(arr.toList) // List(0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9) Quicksort with Mutable Indices
  • 124. USING REFS Quicksort with Mutable Indices def quicksortImperative(a: Array[Int]): Unit = { def swap(i: Int, j: Int): Unit = { val t = a(i) a(i) = a(j) a(j) = t } def sort(l: Int, r: Int): Unit = { val pivot = a((l + r) / 2) var i = l var j = r while (i <= j) { while (a(i) < pivot) i += 1 while (a(j) > pivot) j -= 1 if (i <= j) { swap(i, j) i += 1 j -= 1 } } if (l < j) sort(l, j) if (j < r) sort(i, r) } sort(0, a.length - 1) } val arr = Array(3, 2, 8, 5, 7, 2, 3, 8, 9, 4, 5, 8, 2, 3, 4, 7, 6, 5, 9, 2, 3, 8, 4, 7, 5, 6, 2, 0, 8, 3) runUnsafe(quicksortDefer(arr)) println(arr.toList) // List(0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9) Quicksort with Mutable Indices def quicksortDefer(arr: Array[Int]): ZIO[Any, Nothing, Unit] = { def swap(i: Int, j: Int) = val temp = arr(i) arr(i) = arr(j) arr(j) = temp def sort(l: Int, r: Int): ZIO[Any, Nothing, Unit] = defer(Params(Verify.Lenient)) { val pivot = arr((l + r) / 2) val i = Ref.make(l).run val j = Ref.make(r).run while (i.get.run <= j.get.run) while (arr(i.get.run) < pivot) i.getAndUpdate(i => i + 1).run while (arr(j.get.run) > pivot) j.getAndUpdate(j => j - 1).run if (i.get.run <= j.get.run) swap(i.get.run, j.get.run) i.getAndUpdate(i => i + 1).run j.getAndUpdate(j => j - 1).run if (l < j.get.run) val jv = j.get.run sort(l, jv).run if (j.get.run < r) val iv = i.get.run sort(iv, r).run } sort(0, arr.length - 1) }
  • 125. IN SUMMARY... 1. Introducing ZIO-DIRECT is a ZIO-specific system based on the Monadless paradigm of imperative -> functional transformation. 2. Uses ZIO Features ZIO-DIRECT Correctly interoperates with ZIO Environment and Error Types 3. Can Peek Inside ZIO-DIRECT provides enhanced feedback with .info and .verbose so that you clearly know what code it is generating. 4. Correctness based on Constraints ZIO-DIRECT constrains problematic Scala constructs in order to guarantee correctness. 5. Uses Error Channel ZIO-DIRECT uses the ZIO error channel to mediate try-catch statements. 6. Works well with Mutable Refs ZIO-DIRECT makes the use of ZIO Refs natural and ergonomic.
  • 126. Future Directions val q: Future[(Person, Address)] = defer[Future].from { val p = httpGet[Person]("http://people").run val a = httpGet[Address](s"http://address?owner=${p.id}").run (p, a) } def httpGet[T](str: String): Future[T] val q: List[(Person, Address)] = defer[List].from { val p = people.run val a = p.addresses.run (p, a) } def people: List[Person] val q: Query[(Person, Address)] = defer[Query].from { val p = query[Person].run val a = query[Address].join(a => a.fk == p.id).run (p, a) } def query[T]: Query[T]
  • 127. Thank You Repo: github.com/zio/zio-direct Example Usage: github.com/zio/zio-protoquill/tree/zio-direct