SlideShare a Scribd company logo
1 of 201
Download to read offline
Environmental
effects
A ray tracing
exercise
BeeScala
Ljubljana - 23 Nov 2019
About me
Pierangelo Cecchetto
Scala Consultant - Amsterdam
@pierangelocecc
https://github.com/pierangeloc
This talk
This talk
→ Will cover
This talk
→ Will cover
→ ZIO environment
This talk
→ Will cover
→ ZIO environment
→ Layered computations
This talk
→ Will cover
→ ZIO environment
→ Layered computations
→ Testing
This talk
→ Will cover
→ ZIO environment
→ Layered computations
→ Testing
→ How ray tracing works
This talk
→ Will cover
→ ZIO environment
→ Layered computations
→ Testing
→ How ray tracing works
→ Will not cover
This talk
→ Will cover
→ ZIO environment
→ Layered computations
→ Testing
→ How ray tracing works
→ Will not cover
→ Errors, Concurrency, fibers,
cancellation, runtime
Agenda
Agenda
1. ZIO-101: the bare minimum
Agenda
1. ZIO-101: the bare minimum
2. Build foundations
Agenda
1. ZIO-101: the bare minimum
2. Build foundations
3. Build Ray Tracer components
Agenda
1. ZIO-101: the bare minimum
2. Build foundations
3. Build Ray Tracer components
4. Test Ray tracer components
Agenda
1. ZIO-101: the bare minimum
2. Build foundations
3. Build Ray Tracer components
4. Test Ray tracer components
5. Wiring things together
Agenda
1. ZIO-101: the bare minimum
2. Build foundations
3. Build Ray Tracer components
4. Test Ray tracer components
5. Wiring things together
6. Improving rendering
Agenda
1. ZIO-101: the bare minimum
2. Build foundations
3. Build Ray Tracer components
4. Test Ray tracer components
5. Wiring things together
6. Improving rendering
7. Show pattern at work
ZIO - 101
Program as values
val salutation = console.putStr("Zdravo, ")
val city = console.putStrLn("Ljubljana!!!")
val prg = salutation *> city
salutation.flatMap(_ => city)
// nothing happens!
runtime.unsafeRun(prg)
//> Zdravo, Ljubljana!!!
ZIO - 101
Program as values
val salutation = console.putStr("Zdravo, ")
val city = console.putStrLn("Ljubljana!!!")
val prg = salutation *> city
salutation.flatMap(_ => city)
// nothing happens!
runtime.unsafeRun(prg)
//> Zdravo, Ljubljana!!!
ZIO - 101
Program as values
val salutation = console.putStr("Zdravo, ")
val city = console.putStrLn("Ljubljana!!!")
val prg = salutation *> city
salutation.flatMap(_ => city)
// nothing happens!
runtime.unsafeRun(prg)
//> Zdravo, Ljubljana!!!
ZIO - 101
Program as values
val salutation = console.putStr("Zdravo, ")
val city = console.putStrLn("Ljubljana!!!")
val prg = salutation *> city
salutation.flatMap(_ => city)
// nothing happens!
runtime.unsafeRun(prg)
//> Zdravo, Ljubljana!!!
ZIO - 101
Program as values
val salutation = console.putStr("Zdravo, ")
val city = console.putStrLn("Ljubljana!!!")
val prg = salutation *> city
salutation.flatMap(_ => city)
// nothing happens!
runtime.unsafeRun(prg)
//> Zdravo, Ljubljana!!!
ZIO - 101
Program as values
val salutation = console.putStr("Zdravo, ")
val city = console.putStrLn("Ljubljana!!!")
val prg = salutation *> city
salutation.flatMap(_ => city)
// nothing happens!
runtime.unsafeRun(prg)
//> Zdravo, Ljubljana!!!
ZIO - 101
Program as values
val salutation = console.putStr("Zdravo, ")
val city = console.putStrLn("Ljubljana!!!")
val prg = salutation *> city
salutation.flatMap(_ => city)
// nothing happens!
runtime.unsafeRun(prg)
//> Zdravo, Ljubljana!!!
ZIO - 101
val prg = salutation *> city
val prg: ZIO[Console, Nothing, Unit] = salutation *> city
ZIO[-R, +E, +A]
⬇
R => IO[Either[E, A]]
⬇
R => Either[E, A]
ZIO - 101
val prg = salutation *> city
val prg: ZIO[Console, Nothing, Unit] = salutation *> city
ZIO[-R, +E, +A]
⬇
R => IO[Either[E, A]]
⬇
R => Either[E, A]
ZIO - 101
val prg = salutation *> city
val prg: ZIO[Console, Nothing, Unit] = salutation *> city
ZIO[-R, +E, +A]
⬇
R => IO[Either[E, A]]
⬇
R => Either[E, A]
ZIO - 101
val prg = salutation *> city
val prg: ZIO[Console, Nothing, Unit] = salutation *> city
ZIO[-R, +E, +A]
⬇
R => IO[Either[E, A]]
⬇
R => Either[E, A]
ZIO - 101
val prg: ZIO[Console, Nothing, Unit] = salutation *> city
ZIO - 101
val prg: ZIO[Console, Nothing, Unit] = salutation *> city
→ Needs a Console to run
ZIO - 101
val prg: ZIO[Console, Nothing, Unit] = salutation *> city
→ Needs a Console to run
→ Doesn't produce any error
ZIO - 101
val prg: ZIO[Console, Nothing, Unit] = salutation *> city
→ Needs a Console to run
→ Doesn't produce any error
→ Produce () as output
ZIO - 101
val prg: ZIO[Console, Nothing, Unit] = salutation *> city
val miniRT = new Runtime[Any]{}
// Provide console
val provided = prg.provide(Console.Live)
val provided: ZIO[Any, Nothing, Unit] = prg.provide(Console.Live)
miniRT.unsafeRun(provided)
//> Zdravo, Ljubljana!!!
miniRT.unsafeRun(prg)
// [error] found : zio.ZIO[zio.console.Console,Nothing,Unit]
// [error] required: zio.ZIO[Any,?,?]
ZIO - 101
val prg: ZIO[Console, Nothing, Unit] = salutation *> city
val miniRT = new Runtime[Any]{}
// Provide console
val provided = prg.provide(Console.Live)
val provided: ZIO[Any, Nothing, Unit] = prg.provide(Console.Live)
miniRT.unsafeRun(provided)
//> Zdravo, Ljubljana!!!
miniRT.unsafeRun(prg)
// [error] found : zio.ZIO[zio.console.Console,Nothing,Unit]
// [error] required: zio.ZIO[Any,?,?]
ZIO - 101
val prg: ZIO[Console, Nothing, Unit] = salutation *> city
val miniRT = new Runtime[Any]{}
// Provide console
val provided = prg.provide(Console.Live)
val provided: ZIO[Any, Nothing, Unit] = prg.provide(Console.Live)
miniRT.unsafeRun(provided)
//> Zdravo, Ljubljana!!!
miniRT.unsafeRun(prg)
// [error] found : zio.ZIO[zio.console.Console,Nothing,Unit]
// [error] required: zio.ZIO[Any,?,?]
ZIO - 101
val prg: ZIO[Console, Nothing, Unit] = salutation *> city
val miniRT = new Runtime[Any]{}
// Provide console
val provided = prg.provide(Console.Live)
val provided: ZIO[Any, Nothing, Unit] = prg.provide(Console.Live)
miniRT.unsafeRun(provided)
//> Zdravo, Ljubljana!!!
miniRT.unsafeRun(prg)
// [error] found : zio.ZIO[zio.console.Console,Nothing,Unit]
// [error] required: zio.ZIO[Any,?,?]
ZIO - 101
val prg: ZIO[Console, Nothing, Unit] = salutation *> city
val miniRT = new Runtime[Any]{}
// Provide console
val provided = prg.provide(Console.Live)
val provided: ZIO[Any, Nothing, Unit] = prg.provide(Console.Live)
miniRT.unsafeRun(provided)
//> Zdravo, Ljubljana!!!
miniRT.unsafeRun(prg)
// [error] found : zio.ZIO[zio.console.Console,Nothing,Unit]
// [error] required: zio.ZIO[Any,?,?]
ZIO - 101
val prg: ZIO[Console, Nothing, Unit] = salutation *> city
val miniRT = new Runtime[Any]{}
// Provide console
val provided = prg.provide(Console.Live)
val provided: ZIO[Any, Nothing, Unit] = prg.provide(Console.Live)
miniRT.unsafeRun(provided)
//> Zdravo, Ljubljana!!!
miniRT.unsafeRun(prg)
// [error] found : zio.ZIO[zio.console.Console,Nothing,Unit]
// [error] required: zio.ZIO[Any,?,?]
ZIO - 101
val prg: ZIO[Console, Nothing, Unit] = salutation *> city
val miniRT = new Runtime[Any]{}
// Provide console
val provided = prg.provide(Console.Live)
val provided: ZIO[Any, Nothing, Unit] = prg.provide(Console.Live)
miniRT.unsafeRun(provided)
//> Zdravo, Ljubljana!!!
miniRT.unsafeRun(prg)
// [error] found : zio.ZIO[zio.console.Console,Nothing,Unit]
// [error] required: zio.ZIO[Any,?,?]
ZIO - 101
Environment introduction/elimination
// INTRODUCE AN ENVIRONMENT
ZIO.access(f: R => A): ZIO[R, Nothing, A]
ZIO.accessM(f: R => ZIO[R, E, A]): ZIO[R, E, A]
// ELIMINATE AN ENVIRONMENT
val prg: ZIO[Console, Nothing, Unit]
prg.provide(Console.Live): ZIO[Any, Nothing, Unit]
ZIO - 101
Environment introduction/elimination
// INTRODUCE AN ENVIRONMENT
ZIO.access(f: R => A): ZIO[R, Nothing, A]
ZIO.accessM(f: R => ZIO[R, E, A]): ZIO[R, E, A]
// ELIMINATE AN ENVIRONMENT
val prg: ZIO[Console, Nothing, Unit]
prg.provide(Console.Live): ZIO[Any, Nothing, Unit]
ZIO - 101
Environment introduction/elimination
// INTRODUCE AN ENVIRONMENT
ZIO.access(f: R => A): ZIO[R, Nothing, A]
ZIO.accessM(f: R => ZIO[R, E, A]): ZIO[R, E, A]
// ELIMINATE AN ENVIRONMENT
val prg: ZIO[Console, Nothing, Unit]
prg.provide(Console.Live): ZIO[Any, Nothing, Unit]
ZIO - 101
Environment introduction/elimination
// INTRODUCE AN ENVIRONMENT
ZIO.access(f: R => A): ZIO[R, Nothing, A]
ZIO.accessM(f: R => ZIO[R, E, A]): ZIO[R, E, A]
// ELIMINATE AN ENVIRONMENT
val prg: ZIO[Console, Nothing, Unit]
prg.provide(Console.Live): ZIO[Any, Nothing, Unit]
ZIO - 101
Types at work
type IO[+E, +A] = ZIO[Any, E, A]
type UIO[+A] = ZIO[Any, Nothing, A]
ZIO-101: Module Pattern
Example: A Metrics module (or Algebra)
// the module
trait Metrics {
val metrics: Metrics.Service[Any]
}
object Metrics {
// the service
trait Service[R] {
def inc(label: String): ZIO[R, Nothing, Unit]
}
// the accessor
object > extends Service[Metrics] {
def inc(label: String): ZIO[Metrics, Nothing, Unit] =
ZIO.accessM(_.metrics.inc(label))
}
}
ZIO-101: Module Pattern
Example: A Metrics module (or Algebra)
// the module
trait Metrics {
val metrics: Metrics.Service[Any]
}
object Metrics {
// the service
trait Service[R] {
def inc(label: String): ZIO[R, Nothing, Unit]
}
// the accessor
object > extends Service[Metrics] {
def inc(label: String): ZIO[Metrics, Nothing, Unit] =
ZIO.accessM(_.metrics.inc(label))
}
}
ZIO-101: Module Pattern
Example: A Metrics module (or Algebra)
// the module
trait Metrics {
val metrics: Metrics.Service[Any]
}
object Metrics {
// the service
trait Service[R] {
def inc(label: String): ZIO[R, Nothing, Unit]
}
// the accessor
object > extends Service[Metrics] {
def inc(label: String): ZIO[Metrics, Nothing, Unit] =
ZIO.accessM(_.metrics.inc(label))
}
}
ZIO-101: Module Pattern
A Metrics module - Running
val prg2:
ZIO[Metrics with Log, Nothing, Unit] =
for {
_ <- Log.>.info("Hello")
_ <- Metrics.>.inc("salutation")
_ <- Log.>.info("BeeScala")
_ <- Metrics.>.inc("subject")
} yield ()
trait Prometheus extends Metrics {
val metrics = new Metrics.Service[Any] {
def inc(label: String): ZIO[Any, Nothing, Unit] =
ZIO.effectTotal(counter.labels(label).inc(1))
}
}
miniRT.unsafeRun(
prg2.provide(new Prometheus with Log.Live)
)
ZIO-101: Module Pattern
A Metrics module - Running
val prg2:
ZIO[Metrics with Log, Nothing, Unit] =
for {
_ <- Log.>.info("Hello")
_ <- Metrics.>.inc("salutation")
_ <- Log.>.info("BeeScala")
_ <- Metrics.>.inc("subject")
} yield ()
trait Prometheus extends Metrics {
val metrics = new Metrics.Service[Any] {
def inc(label: String): ZIO[Any, Nothing, Unit] =
ZIO.effectTotal(counter.labels(label).inc(1))
}
}
miniRT.unsafeRun(
prg2.provide(new Prometheus with Log.Live)
)
ZIO-101: Module Pattern
A Metrics module - Running
val prg2:
ZIO[Metrics with Log, Nothing, Unit] =
for {
_ <- Log.>.info("Hello")
_ <- Metrics.>.inc("salutation")
_ <- Log.>.info("BeeScala")
_ <- Metrics.>.inc("subject")
} yield ()
trait Prometheus extends Metrics {
val metrics = new Metrics.Service[Any] {
def inc(label: String): ZIO[Any, Nothing, Unit] =
ZIO.effectTotal(counter.labels(label).inc(1))
}
}
miniRT.unsafeRun(
prg2.provide(new Prometheus with Log.Live)
)
ZIO-101: Module Pattern
A Metrics module - Running
val prg2:
ZIO[Metrics with Log, Nothing, Unit] =
for {
_ <- Log.>.info("Hello")
_ <- Metrics.>.inc("salutation")
_ <- Log.>.info("BeeScala")
_ <- Metrics.>.inc("subject")
} yield ()
trait Prometheus extends Metrics {
val metrics = new Metrics.Service[Any] {
def inc(label: String): ZIO[Any, Nothing, Unit] =
ZIO.effectTotal(counter.labels(label).inc(1))
}
}
miniRT.unsafeRun(
prg2.provide(new Prometheus with Log.Live)
)
ZIO-101: Module Pattern
A Metrics module - Running
val prg2:
ZIO[Metrics with Log, Nothing, Unit] =
for {
_ <- Log.>.info("Hello")
_ <- Metrics.>.inc("salutation")
_ <- Log.>.info("BeeScala")
_ <- Metrics.>.inc("subject")
} yield ()
trait Prometheus extends Metrics {
val metrics = new Metrics.Service[Any] {
def inc(label: String): ZIO[Any, Nothing, Unit] =
ZIO.effectTotal(counter.labels(label).inc(1))
}
}
miniRT.unsafeRun(
prg2.provide(new Prometheus with Log.Live)
)
ZIO-101: Module Pattern
A Metrics module - testing
val prg2: ZIO[Metrics with Log, Nothing, Unit] = /* ... */
case class TestMetrics(incCalls: Ref[List[String]])
extends Metrics.Service[Any] {
def inc(label: String): ZIO[Any, Nothing, Unit] =
incCalls.update(xs => xs :+ label).unit
}
val test = for {
ref <- Ref.make(List[String]())
_ <- prg2.provide(new Log.Live with Metrics {
val metrics = TestMetrics(ref)
})
calls <- ref.get
_ <- UIO.effectTotal(assert(calls == List("salutation", "subject")))
} yield ()
miniRt.unsafeRun(test)
ZIO-101: Module Pattern
A Metrics module - testing
val prg2: ZIO[Metrics with Log, Nothing, Unit] = /* ... */
case class TestMetrics(incCalls: Ref[List[String]])
extends Metrics.Service[Any] {
def inc(label: String): ZIO[Any, Nothing, Unit] =
incCalls.update(xs => xs :+ label).unit
}
val test = for {
ref <- Ref.make(List[String]())
_ <- prg2.provide(new Log.Live with Metrics {
val metrics = TestMetrics(ref)
})
calls <- ref.get
_ <- UIO.effectTotal(assert(calls == List("salutation", "subject")))
} yield ()
miniRt.unsafeRun(test)
ZIO-101: Module Pattern
A Metrics module - testing
val prg2: ZIO[Metrics with Log, Nothing, Unit] = /* ... */
case class TestMetrics(incCalls: Ref[List[String]])
extends Metrics.Service[Any] {
def inc(label: String): ZIO[Any, Nothing, Unit] =
incCalls.update(xs => xs :+ label).unit
}
val test = for {
ref <- Ref.make(List[String]())
_ <- prg2.provide(new Log.Live with Metrics {
val metrics = TestMetrics(ref)
})
calls <- ref.get
_ <- UIO.effectTotal(assert(calls == List("salutation", "subject")))
} yield ()
miniRt.unsafeRun(test)
ZIO-101: Module Pattern
A Metrics module - testing
val prg2: ZIO[Metrics with Log, Nothing, Unit] = /* ... */
case class TestMetrics(incCalls: Ref[List[String]])
extends Metrics.Service[Any] {
def inc(label: String): ZIO[Any, Nothing, Unit] =
incCalls.update(xs => xs :+ label).unit
}
val test = for {
ref <- Ref.make(List[String]())
_ <- prg2.provide(new Log.Live with Metrics {
val metrics = TestMetrics(ref)
})
calls <- ref.get
_ <- UIO.effectTotal(assert(calls == List("salutation", "subject")))
} yield ()
miniRt.unsafeRun(test)
ZIO-101: Module Pattern
A Metrics module - testing
val prg2: ZIO[Metrics with Log, Nothing, Unit] = /* ... */
case class TestMetrics(incCalls: Ref[List[String]])
extends Metrics.Service[Any] {
def inc(label: String): ZIO[Any, Nothing, Unit] =
incCalls.update(xs => xs :+ label).unit
}
val test = for {
ref <- Ref.make(List[String]())
_ <- prg2.provide(new Log.Live with Metrics {
val metrics = TestMetrics(ref)
})
calls <- ref.get
_ <- UIO.effectTotal(assert(calls == List("salutation", "subject")))
} yield ()
miniRt.unsafeRun(test)
ZIO-101: Module Pattern
A Metrics module - testing
val prg2: ZIO[Metrics with Log, Nothing, Unit] = /* ... */
case class TestMetrics(incCalls: Ref[List[String]])
extends Metrics.Service[Any] {
def inc(label: String): ZIO[Any, Nothing, Unit] =
incCalls.update(xs => xs :+ label).unit
}
val test = for {
ref <- Ref.make(List[String]())
_ <- prg2.provide(new Log.Live with Metrics {
val metrics = TestMetrics(ref)
})
calls <- ref.get
_ <- UIO.effectTotal(assert(calls == List("salutation", "subject")))
} yield ()
miniRt.unsafeRun(test)
ZIO-101: Module Pattern
A Metrics module - testing
val prg2: ZIO[Metrics with Log, Nothing, Unit] = /* ... */
case class TestMetrics(incCalls: Ref[List[String]])
extends Metrics.Service[Any] {
def inc(label: String): ZIO[Any, Nothing, Unit] =
incCalls.update(xs => xs :+ label).unit
}
val test = for {
ref <- Ref.make(List[String]())
_ <- prg2.provide(new Log.Live with Metrics {
val metrics = TestMetrics(ref)
})
calls <- ref.get
_ <- UIO.effectTotal(assert(calls == List("salutation", "subject")))
} yield ()
miniRt.unsafeRun(test)
Ray tracing
Why?
Presentation
Book
Ray tracing
Ray tracing
→ World (spheres), light source,
camera
Ray tracing
→ World (spheres), light source,
camera
→ Incident rays
Ray tracing
→ World (spheres), light source,
camera
→ Incident rays
→ Reflected rays
Ray tracing
→ World (spheres), light source,
camera
→ Incident rays
→ Reflected rays
→ Discarded rays
→ Canvas
Ray tracing
→ World (spheres), light source,
camera
→ Incident rays
→ Reflected rays
→ Discarded rays
→ Canvas
→ Colored pixels
Ray tracing
→ World (spheres), light source,
camera
→ Incident rays
→ Reflected rays
→ Discarded rays
→ Canvas
→ Colored pixels
Ray tracing
Options:
Ray tracing
Options:
1. Compute all the rays (and
discard most of them)
Ray tracing
Options:
1. Compute all the rays (and
discard most of them)
2. Compute only the rays outgoing
from the camera through the
canvas, and determine how
they behave on the surfaces
Ray
A ray is defined by the point it
starts from, and its direction
Foundations
Foundations
→ Points and Vectors
Foundations
→ Points and Vectors
→ Transformations (rotate, scale,
translate)
Points and Vectors
case class Vec(x: Double, y: Double, z: Double) {
def +(other: Vec): Vec =
Vec(x + other.x, y + other.y, z + other.z)
def unary_- : Vec =
Vec(-x, -y, -z)
}
case class Pt(x: Double, y: Double, z: Double) {
def -(otherPt: Pt): Vec =
Vec(x - otherPt.x, y - otherPt.y, z - otherPt.z)
def +(vec: Vec) =
Pt(x + vec.x, y + vec.y, z + vec.z)
}
Points and Vectors
case class Vec(x: Double, y: Double, z: Double) {
def +(other: Vec): Vec =
Vec(x + other.x, y + other.y, z + other.z)
def unary_- : Vec =
Vec(-x, -y, -z)
}
case class Pt(x: Double, y: Double, z: Double) {
def -(otherPt: Pt): Vec =
Vec(x - otherPt.x, y - otherPt.y, z - otherPt.z)
def +(vec: Vec) =
Pt(x + vec.x, y + vec.y, z + vec.z)
}
Points and Vectors
zio-test for PBT
testM("vectors form a group")(
check(vecGen, vecGen, vecGen) { (v1, v2, v3) =>
assertApprox (v1 + (v2 + v3), (v1 + v2) + v3) &&
assertApprox (v1 + v2 , v2 + v1) &&
assertApprox (v1 + Vec.zero , Vec.zero + v1)
}
),
testM("vectors and points form an affine space") (
check(ptGen, ptGen) { (p1, p2) =>
assertApprox (p2, p1 + (p2 - p1))
}
)
Points and Vectors
zio-test for PBT
testM("vectors form a group")(
check(vecGen, vecGen, vecGen) { (v1, v2, v3) =>
assertApprox (v1 + (v2 + v3), (v1 + v2) + v3) &&
assertApprox (v1 + v2 , v2 + v1) &&
assertApprox (v1 + Vec.zero , Vec.zero + v1)
}
),
testM("vectors and points form an affine space") (
check(ptGen, ptGen) { (p1, p2) =>
assertApprox (p2, p1 + (p2 - p1))
}
)
Ray
case class Ray(origin: Pt, direction: Vec) {
def positionAt(t: Double): Pt =
origin + (direction * t)
}
Transformations
trait AT
/* Module */
trait ATModule {
val aTModule: ATModule.Service[Any]
}
object ATModule {
/* Service */
trait Service[R] {
def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec]
def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt]
def compose(first: AT, second: AT): ZIO[R, ATError, AT]
}
/* Accessor */
object > extends Service[ATModule] {
def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] =
ZIO.accessM(_.aTModule.applyTf(tf, vec))
def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] =
ZIO.accessM(_.aTModule.applyTf(tf, pt))
def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] =
ZIO.accessM(_.aTModule.compose(first, second))
}
}
Transformations
trait AT
/* Module */
trait ATModule {
val aTModule: ATModule.Service[Any]
}
object ATModule {
/* Service */
trait Service[R] {
def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec]
def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt]
def compose(first: AT, second: AT): ZIO[R, ATError, AT]
}
/* Accessor */
object > extends Service[ATModule] {
def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] =
ZIO.accessM(_.aTModule.applyTf(tf, vec))
def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] =
ZIO.accessM(_.aTModule.applyTf(tf, pt))
def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] =
ZIO.accessM(_.aTModule.compose(first, second))
}
}
Transformations
trait AT
/* Module */
trait ATModule {
val aTModule: ATModule.Service[Any]
}
object ATModule {
/* Service */
trait Service[R] {
def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec]
def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt]
def compose(first: AT, second: AT): ZIO[R, ATError, AT]
}
/* Accessor */
object > extends Service[ATModule] {
def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] =
ZIO.accessM(_.aTModule.applyTf(tf, vec))
def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] =
ZIO.accessM(_.aTModule.applyTf(tf, pt))
def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] =
ZIO.accessM(_.aTModule.compose(first, second))
}
}
Transformations
trait AT
/* Module */
trait ATModule {
val aTModule: ATModule.Service[Any]
}
object ATModule {
/* Service */
trait Service[R] {
def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec]
def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt]
def compose(first: AT, second: AT): ZIO[R, ATError, AT]
}
/* Accessor */
object > extends Service[ATModule] {
def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] =
ZIO.accessM(_.aTModule.applyTf(tf, vec))
def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] =
ZIO.accessM(_.aTModule.applyTf(tf, pt))
def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] =
ZIO.accessM(_.aTModule.compose(first, second))
}
}
Transformations
import zio.macros.annotation.accessible
trait AT
/* Module */
@accessible(">")
trait ATModule {
val aTModule: ATModule.Service[Any]
}
object ATModule {
/* Service */
trait Service[R] {
def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec]
def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt]
def compose(first: AT, second: AT): ZIO[R, ATError, AT]
}
/* Accessor is generated
object > extends Service[ATModule] {
def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] =
ZIO.accessM(_.aTModule.applyTf(tf, vec))
def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] =
ZIO.accessM(_.aTModule.applyTf(tf, pt))
def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] =
ZIO.accessM(_.aTModule.compose(first, second))
}
*/
}
Transformations
trait AT
/* Module */
@accessible(">")
trait ATModule {
val aTModule: ATModule.Service[Any]
}
object ATModule {
/* Service */
trait Service[R] {
def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec]
def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt]
def compose(first: AT, second: AT): ZIO[R, ATError, AT]
}
}
Transformations
val rotatedPt =
for {
rotateX <- ATModule.>.rotateX(math.Pi / 2)
_ <- Log.>.info("rotated of π/2")
res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1))
} yield res
Transformations
val rotatedPt: ZIO[ATModule with Log, ATError, Pt] =
for {
rotateX <- ATModule.>.rotateX(math.Pi / 2)
_ <- Log.>.info("rotated of π/2")
res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1))
} yield res
Transformations - Live
val rotated: ZIO[ATModule, ATError, Vec] =
for {
rotateX <- ATModule.>.rotateZ(math.Pi/2)
res <- ATModule.>.applyTf(rotateX, Vec(x, y, z))
} yield res
Transformations - Live
val rotated: ZIO[ATModule, ATError, Vec] =
for {
rotateX <- ATModule.>.rotateZ(math.Pi/2)
res <- ATModule.>.applyTf(rotateX, Vec(x, y, z))
} yield res
→ Vec(x, y, z)
Transformations - Live
val rotated: ZIO[ATModule, ATError, Vec] =
for {
rotateX <- ATModule.>.rotateZ(math.Pi/2)
res <- ATModule.>.applyTf(rotateX, Vec(x, y, z))
} yield res
→ Vec(x, y, z)
→ Pt(x, y, z)⠀
Transformations - Live
val rotated: ZIO[ATModule, ATError, Vec] =
for {
rotateX <- ATModule.>.rotateZ(math.Pi/2)
res <- ATModule.>.applyTf(rotateX, Vec(x, y, z))
} yield res
→ Vec(x, y, z)
→ Pt(x, y, z)⠀
→ ⠀
Transformations - Live
// Defined somewhere else
object MatrixModule {
trait Service[R] {
def add(m1: M, m2: M): ZIO[R, AlgebraicError, M]
def mul(m1: M, m2: M): ZIO[R, AlgebraicError, M]
}
}
trait Live extends ATModule {
val matrixModule: MatrixModule.Service[Any]
val aTModule: ATModule.Service[Any] = new ATModule.Service[Any] {
def applyTf(tf: AT, vec: Vec): ZIO[Any, AlgebraicError, Vec] =
for {
col <- PointVec.toCol(vec)
colRes <- matrixModule.mul(tf, col)
res <- PointVec.colToVec(colRes)
} yield res
Transformations - Live
// Defined somewhere else
object MatrixModule {
trait Service[R] {
def add(m1: M, m2: M): ZIO[R, AlgebraicError, M]
def mul(m1: M, m2: M): ZIO[R, AlgebraicError, M]
}
}
trait Live extends ATModule {
val matrixModule: MatrixModule.Service[Any]
val aTModule: ATModule.Service[Any] = new ATModule.Service[Any] {
def applyTf(tf: AT, vec: Vec): ZIO[Any, AlgebraicError, Vec] =
for {
col <- PointVec.toCol(vec)
colRes <- matrixModule.mul(tf, col)
res <- PointVec.colToVec(colRes)
} yield res
Transformations - running
val rotated: ZIO[ATModule, ATError, Vec] = ...
val program = rotatedPt.provide(new ATModule.Live{})
// Compiler error:
// object creation impossible, since value matrixModule
// in trait Live of t ype matrix.MatrixModule.Service[Any] is not defined
// [error] rotatedPt.provide(new ATModule.Live{})
Transformations - running
val rotated: ZIO[ATModule, ATError, Vec] = ...
val program = rotatedPt.provide(new ATModule.Live{})
// Compiler error:
// object creation impossible, since value matrixModule
// in trait Live of t ype matrix.MatrixModule.Service[Any] is not defined
// [error] rotatedPt.provide(new ATModule.Live{})
Transformations - running
val rotated: ZIO[ATModule, ATError, Vec] = ...
val program = rotatedPt.provide(new ATModule.Live{})
// Compiler error:
// object creation impossible, since value matrixModule
// in trait Live of t ype matrix.MatrixModule.Service[Any] is not defined
// [error] rotatedPt.provide(new ATModule.Live{})
Transformations - running
val rotated: ZIO[ATModule, ATError, Vec] = ...
val program = rotatedPt.provide(
new ATModule.Live with MatrixModule.BreezeLive
)
// Compiles!
runtime.unsafeRun(program)
// Runs!
Transformations - running
val rotated: ZIO[ATModule, ATError, Vec] = ...
val program = rotatedPt.provide(
new ATModule.Live with MatrixModule.BreezeLive
)
// Compiles!
runtime.unsafeRun(program)
// Runs!
Transformations - running
val rotated: ZIO[ATModule, ATError, Vec] = ...
val program = rotatedPt.provide(
new ATModule.Live with MatrixModule.BreezeLive
)
// Compiles!
runtime.unsafeRun(program)
// Runs!
Layer 1: Transformations
Camera
Everything is relative!
Camera
Everything is relative!
→ Canonical camera: observe always from x = 0 and translate the world by +3
Camera - canonical
case class Camera (
hRes: Int,
vRes: Int,
fieldOfViewRad: Double,
tf: AT
)
Camera - generic
object Camera {
def worldTransformation(from: Pt, to: Pt, up: Vec):
ZIO[ATModule, Nothing, AT] = for {
orientationAT <- // some prepration ...
translateTf <- ATModule.>.translate(-from.x, -from.y, -from.z)
composed <- ATModule.>.compose(translateTf, orientationAT)
} yield composed
def make(
viewFrom: Pt,
viewTo: Pt,
upDirection: Vec,
visualAngleRad: Double,
hRes: Int,
vRes: Int):
ZIO[ATModule, AlgebraicError, Camera] =
worldTransformation(viewFrom, viewTo, upDirection).map {
worldTf => Camera(hRes, vRes, visualAngleRad, worlfTf)
}
Camera - generic
object Camera {
def worldTransformation(from: Pt, to: Pt, up: Vec):
ZIO[ATModule, Nothing, AT] = for {
orientationAT <- // some prepration ...
translateTf <- ATModule.>.translate(-from.x, -from.y, -from.z)
composed <- ATModule.>.compose(translateTf, orientationAT)
} yield composed
def make(
viewFrom: Pt,
viewTo: Pt,
upDirection: Vec,
visualAngleRad: Double,
hRes: Int,
vRes: Int):
ZIO[ATModule, AlgebraicError, Camera] =
worldTransformation(viewFrom, viewTo, upDirection).map {
worldTf => Camera(hRes, vRes, visualAngleRad, worlfTf)
}
Camera - generic
object Camera {
def worldTransformation(from: Pt, to: Pt, up: Vec):
ZIO[ATModule, Nothing, AT] = for {
orientationAT <- // some prepration ...
translateTf <- ATModule.>.translate(-from.x, -from.y, -from.z)
composed <- ATModule.>.compose(translateTf, orientationAT)
} yield composed
def make(
viewFrom: Pt,
viewTo: Pt,
upDirection: Vec,
visualAngleRad: Double,
hRes: Int,
vRes: Int):
ZIO[ATModule, AlgebraicError, Camera] =
worldTransformation(viewFrom, viewTo, upDirection).map {
worldTf => Camera(hRes, vRes, visualAngleRad, worlfTf)
}
Camera - generic
object Camera {
def worldTransformation(from: Pt, to: Pt, up: Vec):
ZIO[ATModule, Nothing, AT] = for {
orientationAT <- // some prepration ...
translateTf <- ATModule.>.translate(-from.x, -from.y, -from.z)
composed <- ATModule.>.compose(translateTf, orientationAT)
} yield composed
def make(
viewFrom: Pt,
viewTo: Pt,
upDirection: Vec,
visualAngleRad: Double,
hRes: Int,
vRes: Int):
ZIO[ATModule, AlgebraicError, Camera] =
worldTransformation(viewFrom, viewTo, upDirection).map {
worldTf => Camera(hRes, vRes, visualAngleRad, worlfTf)
}
World
sealed trait Shape {
def transformation: AT
def material: Material
}
case class Sphere(transformation: AT, material: Material) extends Shape
case class Plane(transformation: AT, material: Material) extends Shape
World
→ Sphere.canonical
sealed trait Shape {
def transformation: AT
def material: Material
}
case class Sphere(transformation: AT, material: Material) extends Shape
case class Plane(transformation: AT, material: Material) extends Shape
World
→ Sphere.canonical
→ Plane.canonical
sealed trait Shape {
def transformation: AT
def material: Material
}
case class Sphere(transformation: AT, material: Material) extends Shape
case class Plane(transformation: AT, material: Material) extends Shape
World
→ Sphere.canonical
→ Plane.canonical
sealed trait Shape {
def transformation: AT
def material: Material
}
case class Sphere(transformation: AT, material: Material) extends Shape
case class Plane(transformation: AT, material: Material) extends Shape
World
→ Sphere.canonical
→ Plane.canonical
sealed trait Shape {
def transformation: AT
def material: Material
}
case class Sphere(transformation: AT, material: Material) extends Shape
case class Plane(transformation: AT, material: Material) extends Shape
World
→ Sphere.canonical
→ Plane.canonical
sealed trait Shape {
def transformation: AT
def material: Material
}
case class Sphere(transformation: AT, material: Material) extends Shape
case class Plane(transformation: AT, material: Material) extends Shape
World
→ Sphere.canonical
→ Plane.canonical
sealed trait Shape {
def transformation: AT
def material: Material
}
case class Sphere(transformation: AT, material: Material) extends Shape
case class Plane(transformation: AT, material: Material) extends Shape
World
Make a world
object Sphere {
def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for {
scale <- ATModule.>.scale(radius, radius, radius)
translate <- ATModule.>.translate(center.x, center.y, center.z)
composed <- ATModule.>.compose(scale, translate)
} yield Sphere(composed, mat)
}
object Plane {
def make(...): ZIO[ATModule, ATError, Plane] = ???
}
case class World(pointLight: PointLight, objects: List[Shape])
World
Make a world
object Sphere {
def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for {
scale <- ATModule.>.scale(radius, radius, radius)
translate <- ATModule.>.translate(center.x, center.y, center.z)
composed <- ATModule.>.compose(scale, translate)
} yield Sphere(composed, mat)
}
object Plane {
def make(...): ZIO[ATModule, ATError, Plane] = ???
}
case class World(pointLight: PointLight, objects: List[Shape])
World
Make a world
object Sphere {
def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for {
scale <- ATModule.>.scale(radius, radius, radius)
translate <- ATModule.>.translate(center.x, center.y, center.z)
composed <- ATModule.>.compose(scale, translate)
} yield Sphere(composed, mat)
}
object Plane {
def make(...): ZIO[ATModule, ATError, Plane] = ???
}
case class World(pointLight: PointLight, objects: List[Shape])
World
Make a world
object Sphere {
def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for {
scale <- ATModule.>.scale(radius, radius, radius)
translate <- ATModule.>.translate(center.x, center.y, center.z)
composed <- ATModule.>.compose(scale, translate)
} yield Sphere(composed, mat)
}
object Plane {
def make(...): ZIO[ATModule, ATError, Plane] = ???
}
case class World(pointLight: PointLight, objects: List[Shape])
World
Make a world
object Sphere {
def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for {
scale <- ATModule.>.scale(radius, radius, radius)
translate <- ATModule.>.translate(center.x, center.y, center.z)
composed <- ATModule.>.compose(scale, translate)
} yield Sphere(composed, mat)
}
object Plane {
def make(...): ZIO[ATModule, ATError, Plane] = ???
}
case class World(pointLight: PointLight, objects: List[Shape])
World
Make a world
object Sphere {
def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for {
scale <- ATModule.>.scale(radius, radius, radius)
translate <- ATModule.>.translate(center.x, center.y, center.z)
composed <- ATModule.>.compose(scale, translate)
} yield Sphere(composed, mat)
}
object Plane {
def make(...): ZIO[ATModule, ATError, Plane] = ???
}
case class World(pointLight: PointLight, objects: List[Shape])
→ Everything requires ATModule
World Rendering - Top Down
Rastering - Generate a stream of colored pixels
@accessible(">")
trait RasteringModule {
val rasteringModule: RasteringModule.Service[Any]
}
object RasteringModule {
trait Service[R] {
def raster(world: World, camera: Camera):
ZStream[R, RayTracerError, ColoredPixel]
}
trait AllWhiteTestRasteringModule extends RasteringModule {
val rasteringModule: Service[Any] = new Service[Any] {
def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] =
for {
x <- ZStream.fromIterable(0 until camera.hRes)
y <- ZStream.fromIterable(0 until camera.vRes)
} yield ColoredPixel(Pixel(x, y), Color.white))
}
World Rendering - Top Down
Rastering - Generate a stream of colored pixels
@accessible(">")
trait RasteringModule {
val rasteringModule: RasteringModule.Service[Any]
}
object RasteringModule {
trait Service[R] {
def raster(world: World, camera: Camera):
ZStream[R, RayTracerError, ColoredPixel]
}
trait AllWhiteTestRasteringModule extends RasteringModule {
val rasteringModule: Service[Any] = new Service[Any] {
def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] =
for {
x <- ZStream.fromIterable(0 until camera.hRes)
y <- ZStream.fromIterable(0 until camera.vRes)
} yield ColoredPixel(Pixel(x, y), Color.white))
}
World Rendering - Top Down
Rastering - Generate a stream of colored pixels
@accessible(">")
trait RasteringModule {
val rasteringModule: RasteringModule.Service[Any]
}
object RasteringModule {
trait Service[R] {
def raster(world: World, camera: Camera):
ZStream[R, RayTracerError, ColoredPixel]
}
trait AllWhiteTestRasteringModule extends RasteringModule {
val rasteringModule: Service[Any] = new Service[Any] {
def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] =
for {
x <- ZStream.fromIterable(0 until camera.hRes)
y <- ZStream.fromIterable(0 until camera.vRes)
} yield ColoredPixel(Pixel(x, y), Color.white))
}
World Rendering - Top Down
Rastering - Live
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering - Live
→ Camera module - Ray per pixel
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering - Live
→ Camera module - Ray per pixel
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering - Live
→ Camera module - Ray per pixel
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering - Live
→ Camera module - Ray per pixel
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering - Live
→ Camera module - Ray per pixel
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
→ World module - Color per ray
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering - Live
→ Camera module - Ray per pixel
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
→ World module - Color per ray
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering - Live
→ Camera module - Ray per pixel
object CameraModule {
trait Service[R] {
def rayForPixel(
camera: Camera, px: Int, py: Int
): ZIO[R, Nothing, Ray]
}
}
→ World module - Color per ray
object WorldModule {
trait Service[R] {
def colorForRay(
world: World, ray: Ray
): ZIO[R, RayTracerError, Color]
}
}
World Rendering - Top Down
Rastering - Live
trait LiveRasteringModule extends RasteringModule {
val cameraModule: CameraModule.Service[Any]
val worldModule: WorldModule.Service[Any]
val rasteringModule: Service[Any] = new Service[Any] {
def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] = {
val pixels: Stream[Nothing, (Int, Int)] = ???
pixels.mapM{
case (px, py) =>
for {
ray <- cameraModule.rayForPixel(camera, px, py)
color <- worldModule.colorForRay(world, ray)
} yield data.ColoredPixel(Pixel(px, py), color)
}
}
}
}
World Rendering - Top Down
Rastering - Live
trait LiveRasteringModule extends RasteringModule {
val cameraModule: CameraModule.Service[Any]
val worldModule: WorldModule.Service[Any]
val rasteringModule: Service[Any] = new Service[Any] {
def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] = {
val pixels: Stream[Nothing, (Int, Int)] = ???
pixels.mapM{
case (px, py) =>
for {
ray <- cameraModule.rayForPixel(camera, px, py)
color <- worldModule.colorForRay(world, ray)
} yield data.ColoredPixel(Pixel(px, py), color)
}
}
}
}
World Rendering - Top Down
Rastering - Live
trait LiveRasteringModule extends RasteringModule {
val cameraModule: CameraModule.Service[Any]
val worldModule: WorldModule.Service[Any]
val rasteringModule: Service[Any] = new Service[Any] {
def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] = {
val pixels: Stream[Nothing, (Int, Int)] = ???
pixels.mapM{
case (px, py) =>
for {
ray <- cameraModule.rayForPixel(camera, px, py)
color <- worldModule.colorForRay(world, ray)
} yield data.ColoredPixel(Pixel(px, py), color)
}
}
}
}
Test LiveRasteringModule
1 - Define the method under test
val world = /* prepare a world */
val camera = /* prepare a camera */
val appUnderTest: ZIO[RasteringModule, RayTracerError, List[ColoredPixel]] =
RasteringModule.>.raster(world, camera)
.flatMap(_.runCollect)
Test LiveRasteringModule
2 - Annotate the modules as mockable
import zio.macros.annotation.mockable
@mockable
trait CameraModule { ... }
@mockable
trait WorldModule { ... }
Test LiveRasteringModule
3 - Build the expectations
val rayForPixelExp: Expectation[CameraModule, Nothing, Ray] =
(CameraModule.rayForPixel(equalTo((camera, 0, 0))) returns value(r1)) *>
(CameraModule.rayForPixel(equalTo((camera, 0, 1))) returns value(r2))
val colorForRayExp: Expectation[WorldModule, Nothing, Color] =
(WorldModule.colorForRay(equalTo((world, r1, 5))) returns value(Color.red)) *>
(WorldModule.colorForRay(equalTo((world, r2, 5))) returns value(Color.green))
Test LiveRasteringModule
4 - Build the environment for the code under test
val appUnderTest: ZIO[RasteringModule, RayTracerError, List[ColoredPixel]] =
RasteringModule.>.raster(world, camera)
.flatMap(_.runCollect)
appUnderTest.provideManaged(
worldModuleExp.managedEnv.zipWith(cameraModuleExp.managedEnv) { (wm, cm) =>
new LiveRasteringModule {
val cameraModule: CameraModule.Service[Any] = cm.cameraModule
val worldModule: WorldModule.Service[Any] = wm.worldModule
}
}
)
Test LiveRasteringModule
4 - Build the environment for the code under test
val appUnderTest: ZIO[RasteringModule, RayTracerError, List[ColoredPixel]] =
RasteringModule.>.raster(world, camera)
.flatMap(_.runCollect)
appUnderTest.provideManaged(
worldModuleExp.managedEnv.zipWith(cameraModuleExp.managedEnv) { (wm, cm) =>
new LiveRasteringModule {
val cameraModule: CameraModule.Service[Any] = cm.cameraModule
val worldModule: WorldModule.Service[Any] = wm.worldModule
}
}
)
Test LiveRasteringModule
5 - Assert on the results
assert(res, equalTo(List(
ColoredPixel(Pixel(0, 0), Color.red),
ColoredPixel(Pixel(0, 1), Color.green),
ColoredPixel(Pixel(1, 0), Color.blue),
ColoredPixel(Pixel(1, 1), Color.white),
))
)
Test LiveRasteringModule
suite("LiveRasteringModule") {
testM("raster should rely on cameraModule and world module") {
val camera = Camera.makeUnsafe(Pt.origin, Pt(0, 0, -1), Vec.uy, math.Pi / 3, 2, 2)
val world = World(PointLight(Pt(5, 5, 5), Color.white), List())
val appUnderTest: ZIO[RasteringModule, RayTracerError, List[ColoredPixel]] =
RasteringModule.>.raster(world, camera).flatMap(_.runCollect)
for {
(worldModuleExp, cameraModuleExp) <- RasteringModuleMocks.mockExpectations(world, camera)
res <- appUnderTest.provideManaged(
worldModuleExp.managedEnv.zipWith(cameraModuleExp.managedEnv) { (wm, cm) =>
new LiveRasteringModule {
val cameraModule: CameraModule.Service[Any] = cm.cameraModule
val worldModule: WorldModule.Service[Any] = wm.worldModule
}
}
)
} yield assert(res, equalTo(List(
ColoredPixel(Pixel(0, 0), Color.red),
ColoredPixel(Pixel(0, 1), Color.green)
)))
}
}
Test
Takeaway: Implement and test every layer only in terms of the
immediately underlying layer
Live CameraModule
trait Live extends CameraModule {
val aTModule: ATModule.Service[Any]
/* implementation */
}
Live WorldModule
WorldTopologyModule
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldModule: Service[Any] = new Service[Any] {
def colorForRay(
world: World, ray: Ray, remaining: Ref[Int]
): ZIO[Any, RayTracerError, Color] = {
/* use other modules */
}
}
}
Live WorldModule
WorldTopologyModule
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldModule: Service[Any] = new Service[Any] {
def colorForRay(
world: World, ray: Ray, remaining: Ref[Int]
): ZIO[Any, RayTracerError, Color] = {
/* use other modules */
}
}
}
Live WorldModule
WorldHitCompsModule
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldHitCompsModule: WorldHitCompsModule.Service[Any]
val worldModule: Service[Any] = new Service[Any] {
def colorForRay(
world: World, ray: Ray, remaining: Ref[Int]
): ZIO[Any, RayTracerError, Color] = {
/* use other modules */
}
}
}
Live WorldModule
WorldHitCompsModule
case class HitComps(
shape: Shape, hitPt: Pt, normalV: Vec, eyeV: Vec, rayReflectV: Vec
)
trait Service[R] {
def hitComps(
ray: Ray, hit: Intersection, intersections: List[Intersection]
): ZIO[R, Nothing, HitComps]
}
Live WorldModule
PhongReflectionModule
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldHitCompsModule: WorldHitCompsModule.Service[Any]
val phongReflectionModule: PhongReflectionModule.Service[Any]
val worldModule: Service[Any] = new Service[Any] {
def colorForRay(
world: World, ray: Ray, remaining: Ref[Int]
): ZIO[Any, RayTracerError, Color] = {
/* use other modules */
}
}
}
Live WorldModule
PhongReflectionModule
case class PhongComponents(
ambient: Color, diffuse: Color, reflective: Color
) {
def toColor: Color = ambient + diffuse + reflective
}
trait BlackWhite extends PhongReflectionModule {
val phongReflectionModule: Service[Any] =
new Service[Any] {
def lighting(
pointLight: PointLight, hitComps: HitComps, inShadow: Boolean
): UIO[PhongComponents] = {
if (inShadow) UIO(PhongComponents.allBlack)
else UIO(PhongComponents.allWhite)
}
}
}
Live WorldModule
PhongReflectionModule
case class PhongComponents(
ambient: Color, diffuse: Color, reflective: Color
) {
def toColor: Color = ambient + diffuse + reflective
}
trait BlackWhite extends PhongReflectionModule {
val phongReflectionModule: Service[Any] =
new Service[Any] {
def lighting(
pointLight: PointLight, hitComps: HitComps, inShadow: Boolean
): UIO[PhongComponents] = {
if (inShadow) UIO(PhongComponents.allBlack)
else UIO(PhongComponents.allWhite)
}
}
}
Display the first canvas / 1
def drawOnCanvasWithCamera(world: World, camera: Camera, canvas: Canvas):
ZIO[RasteringModule, RayTracerError, Unit] =
for {
coloredPointsStream <- RasteringModule.>.raster(world, camera)
_ <- coloredPointsStream.mapM(cp => canvas.update(cp)).run(Sink.drain)
} yield ()
def program(viewFrom: Pt):
ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit] =
for {
camera <- cameraFor(viewFrom: Pt)
w <- world
canvas <- Canvas.create()
_ <- drawOnCanvasWithCamera(w, camera, canvas)
_ <- CanvasSerializer.>.serialize(canvas, 255)
} yield ()
Display the first canvas / 2
def program(viewFrom: Pt):
ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit]
program(Pt(2, 2, -10))
.provide(
new CanvasSerializer.PPMCanvasSerializer
with RasteringModule.ChunkRasteringModule
with ATModule.Live
)
// Members declared in zio.blocking.Blocking
// [error] val blocking: zio.blocking.Blocking.Service[Any] = ???
// [error]
// [error] // Members declared in modules.RasteringModule.ChunkRasteringModule
// [error] val cameraModule: modules.CameraModule.Service[Any] = ???
// [error] val worldModule: modules.WorldModule.Service[Any] = ???
// [error]
// [error] // Members declared in geometry.affine.ATModule.Live
// [error] val matrixModule: geometry.matrix.MatrixModule.Service[Any] = ???
Display the first canvas / 2
def program(viewFrom: Pt):
ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit]
program(Pt(2, 2, -10))
.provide(
new CanvasSerializer.PPMCanvasSerializer
with RasteringModule.ChunkRasteringModule
with ATModule.Live
)
// Members declared in zio.blocking.Blocking
// [error] val blocking: zio.blocking.Blocking.Service[Any] = ???
// [error]
// [error] // Members declared in modules.RasteringModule.ChunkRasteringModule
// [error] val cameraModule: modules.CameraModule.Service[Any] = ???
// [error] val worldModule: modules.WorldModule.Service[Any] = ???
// [error]
// [error] // Members declared in geometry.affine.ATModule.Live
// [error] val matrixModule: geometry.matrix.MatrixModule.Service[Any] = ???
Display the first canvas / 2
def program(viewFrom: Pt):
ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit]
program(Pt(2, 2, -10))
.provide(
new CanvasSerializer.PPMCanvasSerializer
with RasteringModule.ChunkRasteringModule
with ATModule.Live
)
// Members declared in zio.blocking.Blocking
// [error] val blocking: zio.blocking.Blocking.Service[Any] = ???
// [error]
// [error] // Members declared in modules.RasteringModule.ChunkRasteringModule
// [error] val cameraModule: modules.CameraModule.Service[Any] = ???
// [error] val worldModule: modules.WorldModule.Service[Any] = ???
// [error]
// [error] // Members declared in geometry.affine.ATModule.Live
// [error] val matrixModule: geometry.matrix.MatrixModule.Service[Any] = ???
Display the first canvas / 3
def program(viewFrom: Pt):
ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit]
program(Pt(2, 2, -10))
.provide(
new CanvasSerializer.PPMCanvasSerializer
with RasteringModule.ChunkRasteringModule
with ATModule.Live
with CameraModule.Live
with MatrixModule.BreezeLive
with WorldModule.Live
)
)
// [error] // Members declared in io.tuliplogic.raytracer.ops.model.modules.WorldModule.Live
// [error] val phongReflectionModule: io.tuliplogic.raytracer.ops.model.modules.PhongReflectionModule.Service[Any] = ???
// [error] val worldHitCompsModule: io.tuliplogic.raytracer.ops.model.modules.WorldHitCompsModule.Service[Any] = ???
// [error] val worldReflectionModule: io.tuliplogic.raytracer.ops.model.modules.WorldReflectionModule.Service[Any] = ???
// [error] val worldRefractionModule: io.tuliplogic.raytracer.ops.model.modules.WorldRefractionModule.Service[Any] = ???
// [error] val worldTopologyModule: io.tuliplogic.raytracer.ops.model.modules.WorldTopologyModule.Service[Any] = ???
Display the first canvas - /4
Group modules in trait
trait BasicModules extends
NormalReflectModule.Live
with RayModule.Live
with ATModule.Live
with MatrixModule.BreezeLive
with WorldModule.Live
with WorldTopologyModule.Live
with WorldHitCompsModule.Live
with CameraModule.Live
with RasteringModule.Live
with Blocking.Live
Display the first canvas - /4
Group modules in trait
trait BasicModules extends
NormalReflectModule.Live
with RayModule.Live
with ATModule.Live
with MatrixModule.BreezeLive
with WorldModule.Live
with WorldTopologyModule.Live
with WorldHitCompsModule.Live
with CameraModule.Live
with RasteringModule.Live
with Blocking.Live
Display the first canvas - /5
How to group typeclasses?
program[F[_]
: NormalReflectModule
: RayModule
: ATModule
: MatrixModule
: WorldModule
: WorldTopologyModule
: WorldHitCompsModule
: CameraModule
: RasteringModule
: Blocking
]
program[F[_]: BasicModules] //not so easy
Display the first canvas - /6
Group modules
def program(viewFrom: Pt):
ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit]
program(Pt(2, 2, -10))
.provide(new BasicModules with PhongReflectionModule.BlackWhite)
Display the first canvas - /7
Group modules
def program(viewFrom: Pt):
ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit]
def run(args: List[String]): ZIO[ZEnv, Nothing, Int] =
ZIO.traverse(-18 to -6)(z => program(Pt(2, 2, z))
.provide(
new BasicModules with PhongReflectionModule.BlackWhite
)
).foldM(err =>
console.putStrLn(s"Execution failed with: $err").as(1),
_ => UIO.succeed(0)
)
Live PhongReflectionModule
trait Live extends PhongReflectionModule {
val aTModule: ATModule.Service[Any]
val normalReflectModule: NormalReflectModule.Service[Any]
val lightDiffusionModule: LightDiffusionModule.Service[Any]
}
Live PhongReflectionModule
program(Pt(2, 2, -10))
.provide(
new BasicModules
with PhongReflectionModule.Live
// with PhongReflectionModule.BlackWhite
)
Live PhongReflectionModule
program(Pt(2, 2, -10))
.provide(
new BasicModules
with PhongReflectionModule.Live
// with PhongReflectionModule.BlackWhite
)
Live PhongReflectionModule
program(Pt(2, 2, -10))
.provide(
new BasicModules
with PhongReflectionModule.Live
// with PhongReflectionModule.BlackWhite
)
Live PhongReflectionModule
program(Pt(2, 2, -10))
.provide(
new BasicModules
with PhongReflectionModule.Live
// with PhongReflectionModule.BlackWhite
)
Reflect the ligtht source
Describe material properties
case class Material(
color: Color, // the basic color
ambient: Double, // ∈ [0, 1]
diffuse: Double, // ∈ [0, 1]
specular: Double, // ∈ [0, 1]
shininess: Double, // ∈ [10, 200]
)
Live PhongReflectionModule
trait Live extends PhongReflectionModule {
val aTModule: ATModule.Service[Any]
val normalReflectModule: NormalReflectModule.Service[Any]
val lightDiffusionModule: LightDiffusionModule.Service[Any]
val lightReflectionModule: LightReflectionModule.Service[Any]
}
Live PhongReflectionModule
trait Live extends PhongReflectionModule {
val aTModule: ATModule.Service[Any]
val normalReflectModule: NormalReflectModule.Service[Any]
val lightDiffusionModule: LightDiffusionModule.Service[Any]
val lightReflectionModule: LightReflectionModule.Service[Any]
}
Render 3 spheres - reflect light source
case class Material(
pattern: Pattern, // the color pattern
ambient: Double, // ∈ [0, 1]
diffuse: Double, // ∈ [0, 1]
specular: Double, // ∈ [0, 1]
shininess: Double, // ∈ [10, 200]
reflective: Double, // ∈ [0, 1]
)
trait Live extends PhongReflectionModule {
/* other modules */
val lightReflectionModule: LightReflectionModule.Service[Any]
}
trait RenderingModulesV1
extends PhongReflectionModule.Live
with LightReflectionModule.Live
program(
from = Pt(57, 20, z),
to = Pt(20, 0, 20)
).provide {
new BasicModules
with RenderingModulesV1
}
Render 3 spheres - reflect light source
case class Material(
pattern: Pattern, // the color pattern
ambient: Double, // ∈ [0, 1]
diffuse: Double, // ∈ [0, 1]
specular: Double, // ∈ [0, 1]
shininess: Double, // ∈ [10, 200]
reflective: Double, // ∈ [0, 1]
)
trait Live extends PhongReflectionModule {
/* other modules */
val lightReflectionModule: LightReflectionModule.Service[Any]
}
trait RenderingModulesV1
extends PhongReflectionModule.Live
with LightReflectionModule.Live
program(
from = Pt(57, 20, z),
to = Pt(20, 0, 20)
).provide {
new BasicModules
with RenderingModulesV1
}
Render 3 spheres - reflect light source
case class Material(
pattern: Pattern, // the color pattern
ambient: Double, // ∈ [0, 1]
diffuse: Double, // ∈ [0, 1]
specular: Double, // ∈ [0, 1]
shininess: Double, // ∈ [10, 200]
reflective: Double, // ∈ [0, 1]
)
trait Live extends PhongReflectionModule {
/* other modules */
val lightReflectionModule: LightReflectionModule.Service[Any]
}
trait RenderingModulesV1
extends PhongReflectionModule.Live
with LightReflectionModule.Live
program(
from = Pt(57, 20, z),
to = Pt(20, 0, 20)
).provide {
new BasicModules
with RenderingModulesV1
}
Render 3 spheres - reflect light source
case class Material(
pattern: Pattern, // the color pattern
ambient: Double, // ∈ [0, 1]
diffuse: Double, // ∈ [0, 1]
specular: Double, // ∈ [0, 1]
shininess: Double, // ∈ [10, 200]
reflective: Double, // ∈ [0, 1]
)
trait Live extends PhongReflectionModule {
/* other modules */
val lightReflectionModule: LightReflectionModule.Service[Any]
}
trait RenderingModulesV1
extends PhongReflectionModule.Live
with LightReflectionModule.Live
program(
from = Pt(57, 20, z),
to = Pt(20, 0, 20)
).provide {
new BasicModules
with RenderingModulesV1
}
Reflective surfaces
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldHitCompsModule: WorldHitCompsModule.Service[Any]
val phongReflectionModule: PhongReflectionModule.Service[Any]
val worldReflectionModule: WorldReflectionModule.Service[Any]
Reflective surfaces
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldHitCompsModule: WorldHitCompsModule.Service[Any]
val phongReflectionModule: PhongReflectionModule.Service[Any]
val worldReflectionModule: WorldReflectionModule.Service[Any]
val worldModule: Service[Any] =
new Service[Any] {
def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] =
{
/* ... */
for {
color <- /* standard computation of color */
reflectedColor <- worldReflectionModule.reflectedColor(world, hc)
} yield color + reflectedColor
}
}
}
Reflective surfaces
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldHitCompsModule: WorldHitCompsModule.Service[Any]
val phongReflectionModule: PhongReflectionModule.Service[Any]
val worldReflectionModule: WorldReflectionModule.Service[Any]
val worldModule: Service[Any] =
new Service[Any] {
def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] =
{
/* ... */
for {
color <- /* standard computation of color */
reflectedColor <- worldReflectionModule.reflectedColor(world, hc)
} yield color + reflectedColor
}
}
}
Reflective surfaces
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldHitCompsModule: WorldHitCompsModule.Service[Any]
val phongReflectionModule: PhongReflectionModule.Service[Any]
val worldReflectionModule: WorldReflectionModule.Service[Any]
val worldModule: Service[Any] =
new Service[Any] {
def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] =
{
/* ... */
for {
color <- /* standard computation of color */
reflectedColor <- worldReflectionModule.reflectedColor(world, hc)
} yield color + reflectedColor
}
}
}
Reflective surfaces
trait Live extends WorldModule {
val worldTopologyModule: WorldTopologyModule.Service[Any]
val worldHitCompsModule: WorldHitCompsModule.Service[Any]
val phongReflectionModule: PhongReflectionModule.Service[Any]
val worldReflectionModule: WorldReflectionModule.Service[Any]
val worldModule: Service[Any] =
new Service[Any] {
def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] =
{
/* ... */
for {
color <- /* standard computation of color */
reflectedColor <- worldReflectionModule.reflectedColor(world, hc)
} yield color + reflectedColor
}
}
}
Handling reflection - Live
trait Live extends WorldReflectionModule {
val worldModule: WorldModule.Service[Any]
val worldReflectionModule = new WorldReflectionModule.Service[Any] {
def reflectedColor(world: World, hitComps: HitComps, remaining: Int):
ZIO[Any, RayTracerError, Color] =
if (hitComps.shape.material.reflective == 0) {
UIO(Color.black)
} else {
val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV)
worldModule.colorForRay(world, reflRay, remaining).map(c =>
c * hitComps.shape.material.reflective
)
}
}
}
Handling reflection - Live
trait Live extends WorldReflectionModule {
val worldModule: WorldModule.Service[Any]
val worldReflectionModule = new WorldReflectionModule.Service[Any] {
def reflectedColor(world: World, hitComps: HitComps, remaining: Int):
ZIO[Any, RayTracerError, Color] =
if (hitComps.shape.material.reflective == 0) {
UIO(Color.black)
} else {
val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV)
worldModule.colorForRay(world, reflRay, remaining).map(c =>
c * hitComps.shape.material.reflective
)
}
}
}
Handling reflection - Live
trait Live extends WorldReflectionModule {
val worldModule: WorldModule.Service[Any]
val worldReflectionModule = new WorldReflectionModule.Service[Any] {
def reflectedColor(world: World, hitComps: HitComps, remaining: Int):
ZIO[Any, RayTracerError, Color] =
if (hitComps.shape.material.reflective == 0) {
UIO(Color.black)
} else {
val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV)
worldModule.colorForRay(world, reflRay, remaining).map(c =>
c * hitComps.shape.material.reflective
)
}
}
}
Handling reflection - Live
trait Live extends WorldReflectionModule {
val worldModule: WorldModule.Service[Any]
val worldReflectionModule = new WorldReflectionModule.Service[Any] {
def reflectedColor(world: World, hitComps: HitComps, remaining: Int):
ZIO[Any, RayTracerError, Color] =
if (hitComps.shape.material.reflective == 0) {
UIO(Color.black)
} else {
val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV)
worldModule.colorForRay(world, reflRay, remaining).map(c =>
c * hitComps.shape.material.reflective
)
}
}
}
Reflective surfaces
Circular dependency
Handling reflection - Noop module
trait NoReflectionModule extends WorldReflectionModule {
val worldReflectionModule = new WorldReflectionModule.Service[Any] {
def reflectedColor(
world: World,
hitComps: HitComps,
remaining: Int
): ZIO[Any, RayTracerError, Color] = UIO.succeed(Color.black)
}
}
Handling reflection - Noop module
→ Red: reflective = 0.9
→ Green/white: reflective = 0.6
program(
from = Pt(57, 20, z),
to = Pt(20, 0, 20)
).provide {
new BasicModules
with PhongReflectionModule.Live
with WorldReflectionModule.NoReflectionModule
}
Handling reflection - Live module
→ Red: reflective = 0.9
→ Green/white: reflective = 0.6
program(
from = Pt(57, 20, z),
to = Pt(20, 0, 20)
).provide {
new BasicModules
with PhongReflectionModule.Live
with WorldReflectionModule.Live⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
}
Alternative approach
Provide partial environments
val program: ZIO[RasteringModule, Nothing, Unit]
val partial = program
.provideSome[WorldReflectionModule](
f: WorldReflectionModule => RasteringModule
): ZIO[WorldReflectionModule, E, A]
partial.provide(new WorldReflectionModule.Live)
Conclusion - Environmental Effects
ZIO[R, E, A]
Build purely functional, testable, modular applications
Conclusion - Environmental Effects
ZIO[R, E, A]
Build purely functional, testable, modular applications
→ Do not require HKT, typeclasses, etc
Conclusion - Environmental Effects
ZIO[R, E, A]
Build purely functional, testable, modular applications
→ Do not require HKT, typeclasses, etc
→ Do not abuse typeclasses
Conclusion - Environmental Effects
ZIO[R, E, A]
Build purely functional, testable, modular applications
→ Do not require HKT, typeclasses, etc
→ Do not abuse typeclasses
→ Can group capabilities
Conclusion - Environmental Effects
ZIO[R, E, A]
Build purely functional, testable, modular applications
→ Do not require HKT, typeclasses, etc
→ Do not abuse typeclasses
→ Can group capabilities
→ Can provide capabilities one at a time
Conclusion - Environmental Effects
ZIO[R, E, A]
Build purely functional, testable, modular applications
→ Do not require HKT, typeclasses, etc
→ Do not abuse typeclasses
→ Can group capabilities
→ Can provide capabilities one at a time
→ Are not dependent on implicits (survive refactoring)
Conclusion - Environmental Effects
Conclusion - Environmental Effects
→ Low entry barrier, very mechanical
Conclusion - Environmental Effects
→ Low entry barrier, very mechanical
→ Macros help with boilerplate
Conclusion - Environmental Effects
→ Low entry barrier, very mechanical
→ Macros help with boilerplate
→ Handle circular dependencies
Conclusion - Environmental Effects
→ Low entry barrier, very mechanical
→ Macros help with boilerplate
→ Handle circular dependencies
→ Try it out!
$
Conclusion - Environmental Effects
→ Low entry barrier, very mechanical
→ Macros help with boilerplate
→ Handle circular dependencies
→ Try it out!
$
→ Join ZIO Discord channel
Thank you! 1
Questions?
@pierangelocecc
https://github.com/pierangeloc
1 
Ray Tracing with ZIO

More Related Content

What's hot

KCDC 2018 - Rapid API Development with Sails
KCDC 2018 - Rapid API Development with SailsKCDC 2018 - Rapid API Development with Sails
KCDC 2018 - Rapid API Development with SailsJustin James
 
Architecture Patterns in Practice with Kotlin. UA Mobile 2017.
Architecture Patterns in Practice with Kotlin. UA Mobile 2017.Architecture Patterns in Practice with Kotlin. UA Mobile 2017.
Architecture Patterns in Practice with Kotlin. UA Mobile 2017.UA Mobile
 
The algebra of library design
The algebra of library designThe algebra of library design
The algebra of library designLeonardo Borges
 
20110424 action scriptを使わないflash勉強会
20110424 action scriptを使わないflash勉強会20110424 action scriptを使わないflash勉強会
20110424 action scriptを使わないflash勉強会Hiroki Mizuno
 
Javascript Common Mistakes
Javascript Common MistakesJavascript Common Mistakes
Javascript Common Mistakes동수 장
 
Core Audio in iOS 6 (CocoaConf Chicago, March 2013)
Core Audio in iOS 6 (CocoaConf Chicago, March 2013)Core Audio in iOS 6 (CocoaConf Chicago, March 2013)
Core Audio in iOS 6 (CocoaConf Chicago, March 2013)Chris Adamson
 
Scottish Ruby Conference 2010 Arduino, Ruby RAD
Scottish Ruby Conference 2010 Arduino, Ruby RADScottish Ruby Conference 2010 Arduino, Ruby RAD
Scottish Ruby Conference 2010 Arduino, Ruby RADlostcaggy
 
Non stop random2b
Non stop random2bNon stop random2b
Non stop random2bphanhung20
 
The 2016 Android Developer Toolbox [MOBILIZATION]
The 2016 Android Developer Toolbox [MOBILIZATION]The 2016 Android Developer Toolbox [MOBILIZATION]
The 2016 Android Developer Toolbox [MOBILIZATION]Nilhcem
 
PenO 3 2014 sessie 2
PenO 3 2014 sessie 2PenO 3 2014 sessie 2
PenO 3 2014 sessie 2Sven Charleer
 
Android Wear Essentials
Android Wear EssentialsAndroid Wear Essentials
Android Wear EssentialsNilhcem
 
ng-conf 2017: Angular Mischief Maker Slides
ng-conf 2017: Angular Mischief Maker Slidesng-conf 2017: Angular Mischief Maker Slides
ng-conf 2017: Angular Mischief Maker SlidesLukas Ruebbelke
 
Testing for Unicorns
Testing for UnicornsTesting for Unicorns
Testing for UnicornsAlex Soto
 

What's hot (13)

KCDC 2018 - Rapid API Development with Sails
KCDC 2018 - Rapid API Development with SailsKCDC 2018 - Rapid API Development with Sails
KCDC 2018 - Rapid API Development with Sails
 
Architecture Patterns in Practice with Kotlin. UA Mobile 2017.
Architecture Patterns in Practice with Kotlin. UA Mobile 2017.Architecture Patterns in Practice with Kotlin. UA Mobile 2017.
Architecture Patterns in Practice with Kotlin. UA Mobile 2017.
 
The algebra of library design
The algebra of library designThe algebra of library design
The algebra of library design
 
20110424 action scriptを使わないflash勉強会
20110424 action scriptを使わないflash勉強会20110424 action scriptを使わないflash勉強会
20110424 action scriptを使わないflash勉強会
 
Javascript Common Mistakes
Javascript Common MistakesJavascript Common Mistakes
Javascript Common Mistakes
 
Core Audio in iOS 6 (CocoaConf Chicago, March 2013)
Core Audio in iOS 6 (CocoaConf Chicago, March 2013)Core Audio in iOS 6 (CocoaConf Chicago, March 2013)
Core Audio in iOS 6 (CocoaConf Chicago, March 2013)
 
Scottish Ruby Conference 2010 Arduino, Ruby RAD
Scottish Ruby Conference 2010 Arduino, Ruby RADScottish Ruby Conference 2010 Arduino, Ruby RAD
Scottish Ruby Conference 2010 Arduino, Ruby RAD
 
Non stop random2b
Non stop random2bNon stop random2b
Non stop random2b
 
The 2016 Android Developer Toolbox [MOBILIZATION]
The 2016 Android Developer Toolbox [MOBILIZATION]The 2016 Android Developer Toolbox [MOBILIZATION]
The 2016 Android Developer Toolbox [MOBILIZATION]
 
PenO 3 2014 sessie 2
PenO 3 2014 sessie 2PenO 3 2014 sessie 2
PenO 3 2014 sessie 2
 
Android Wear Essentials
Android Wear EssentialsAndroid Wear Essentials
Android Wear Essentials
 
ng-conf 2017: Angular Mischief Maker Slides
ng-conf 2017: Angular Mischief Maker Slidesng-conf 2017: Angular Mischief Maker Slides
ng-conf 2017: Angular Mischief Maker Slides
 
Testing for Unicorns
Testing for UnicornsTesting for Unicorns
Testing for Unicorns
 

Similar to Environmental effects - a ray tracing exercise

ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaWiem Zine Elabidine
 
Zope component architechture
Zope component architechtureZope component architechture
Zope component architechtureAnatoly Bubenkov
 
20191116 custom operators in swift
20191116 custom operators in swift20191116 custom operators in swift
20191116 custom operators in swiftChiwon Song
 
Future vs. Monix Task
Future vs. Monix TaskFuture vs. Monix Task
Future vs. Monix TaskHermann Hueck
 
2011 py con
2011 py con2011 py con
2011 py conEing Ong
 
Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in ScalaHermann Hueck
 
Kernel Recipes 2016 - Why you need a test strategy for your kernel development
Kernel Recipes 2016 - Why you need a test strategy for your kernel developmentKernel Recipes 2016 - Why you need a test strategy for your kernel development
Kernel Recipes 2016 - Why you need a test strategy for your kernel developmentAnne Nicolas
 
Django productivity tips and tricks
Django productivity tips and tricksDjango productivity tips and tricks
Django productivity tips and tricksSimone Federici
 
Playing With Fire - An Introduction to Node.js
Playing With Fire - An Introduction to Node.jsPlaying With Fire - An Introduction to Node.js
Playing With Fire - An Introduction to Node.jsMike Hagedorn
 
The Ring programming language version 1.7 book - Part 85 of 196
The Ring programming language version 1.7 book - Part 85 of 196The Ring programming language version 1.7 book - Part 85 of 196
The Ring programming language version 1.7 book - Part 85 of 196Mahmoud Samir Fayed
 
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional ProgrammingZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional ProgrammingJohn De Goes
 
The Ring programming language version 1.5.3 book - Part 89 of 184
The Ring programming language version 1.5.3 book - Part 89 of 184The Ring programming language version 1.5.3 book - Part 89 of 184
The Ring programming language version 1.5.3 book - Part 89 of 184Mahmoud Samir Fayed
 
Alexander Reelsen - Seccomp for Developers
Alexander Reelsen - Seccomp for DevelopersAlexander Reelsen - Seccomp for Developers
Alexander Reelsen - Seccomp for DevelopersDevDay Dresden
 
YAPC::Brasil 2009, POE
YAPC::Brasil 2009, POEYAPC::Brasil 2009, POE
YAPC::Brasil 2009, POEThiago Rondon
 
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
 

Similar to Environmental effects - a ray tracing exercise (20)

Ray tracing with ZIO-ZLayer
Ray tracing with ZIO-ZLayerRay tracing with ZIO-ZLayer
Ray tracing with ZIO-ZLayer
 
ZIO Queue
ZIO QueueZIO Queue
ZIO Queue
 
ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in Scala
 
Berlin meetup
Berlin meetupBerlin meetup
Berlin meetup
 
Zope component architechture
Zope component architechtureZope component architechture
Zope component architechture
 
20191116 custom operators in swift
20191116 custom operators in swift20191116 custom operators in swift
20191116 custom operators in swift
 
Future vs. Monix Task
Future vs. Monix TaskFuture vs. Monix Task
Future vs. Monix Task
 
2011 py con
2011 py con2011 py con
2011 py con
 
Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in Scala
 
Kernel Recipes 2016 - Why you need a test strategy for your kernel development
Kernel Recipes 2016 - Why you need a test strategy for your kernel developmentKernel Recipes 2016 - Why you need a test strategy for your kernel development
Kernel Recipes 2016 - Why you need a test strategy for your kernel development
 
Django productivity tips and tricks
Django productivity tips and tricksDjango productivity tips and tricks
Django productivity tips and tricks
 
Playing With Fire - An Introduction to Node.js
Playing With Fire - An Introduction to Node.jsPlaying With Fire - An Introduction to Node.js
Playing With Fire - An Introduction to Node.js
 
The Ring programming language version 1.7 book - Part 85 of 196
The Ring programming language version 1.7 book - Part 85 of 196The Ring programming language version 1.7 book - Part 85 of 196
The Ring programming language version 1.7 book - Part 85 of 196
 
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional ProgrammingZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
 
The Ring programming language version 1.5.3 book - Part 89 of 184
The Ring programming language version 1.5.3 book - Part 89 of 184The Ring programming language version 1.5.3 book - Part 89 of 184
The Ring programming language version 1.5.3 book - Part 89 of 184
 
Alexander Reelsen - Seccomp for Developers
Alexander Reelsen - Seccomp for DevelopersAlexander Reelsen - Seccomp for Developers
Alexander Reelsen - Seccomp for Developers
 
YAPC::Brasil 2009, POE
YAPC::Brasil 2009, POEYAPC::Brasil 2009, POE
YAPC::Brasil 2009, POE
 
Zio from Home
Zio from Home Zio from Home
Zio from Home
 
One Monad to Rule Them All
One Monad to Rule Them AllOne Monad to Rule Them All
One Monad to Rule Them All
 
ZIO Queue
ZIO QueueZIO Queue
ZIO Queue
 

Recently uploaded

Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxOnBoard
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAndikSusilo4
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxMaking_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxnull - The Open Security Community
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 

Recently uploaded (20)

Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptxVulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptx
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & Application
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxMaking_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
The transition to renewables in India.pdf
The transition to renewables in India.pdfThe transition to renewables in India.pdf
The transition to renewables in India.pdf
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 

Environmental effects - a ray tracing exercise

  • 2. About me Pierangelo Cecchetto Scala Consultant - Amsterdam @pierangelocecc https://github.com/pierangeloc
  • 5. This talk → Will cover → ZIO environment
  • 6. This talk → Will cover → ZIO environment → Layered computations
  • 7. This talk → Will cover → ZIO environment → Layered computations → Testing
  • 8. This talk → Will cover → ZIO environment → Layered computations → Testing → How ray tracing works
  • 9. This talk → Will cover → ZIO environment → Layered computations → Testing → How ray tracing works → Will not cover
  • 10. This talk → Will cover → ZIO environment → Layered computations → Testing → How ray tracing works → Will not cover → Errors, Concurrency, fibers, cancellation, runtime
  • 12. Agenda 1. ZIO-101: the bare minimum
  • 13. Agenda 1. ZIO-101: the bare minimum 2. Build foundations
  • 14. Agenda 1. ZIO-101: the bare minimum 2. Build foundations 3. Build Ray Tracer components
  • 15. Agenda 1. ZIO-101: the bare minimum 2. Build foundations 3. Build Ray Tracer components 4. Test Ray tracer components
  • 16. Agenda 1. ZIO-101: the bare minimum 2. Build foundations 3. Build Ray Tracer components 4. Test Ray tracer components 5. Wiring things together
  • 17. Agenda 1. ZIO-101: the bare minimum 2. Build foundations 3. Build Ray Tracer components 4. Test Ray tracer components 5. Wiring things together 6. Improving rendering
  • 18. Agenda 1. ZIO-101: the bare minimum 2. Build foundations 3. Build Ray Tracer components 4. Test Ray tracer components 5. Wiring things together 6. Improving rendering 7. Show pattern at work
  • 19. ZIO - 101 Program as values val salutation = console.putStr("Zdravo, ") val city = console.putStrLn("Ljubljana!!!") val prg = salutation *> city salutation.flatMap(_ => city) // nothing happens! runtime.unsafeRun(prg) //> Zdravo, Ljubljana!!!
  • 20. ZIO - 101 Program as values val salutation = console.putStr("Zdravo, ") val city = console.putStrLn("Ljubljana!!!") val prg = salutation *> city salutation.flatMap(_ => city) // nothing happens! runtime.unsafeRun(prg) //> Zdravo, Ljubljana!!!
  • 21. ZIO - 101 Program as values val salutation = console.putStr("Zdravo, ") val city = console.putStrLn("Ljubljana!!!") val prg = salutation *> city salutation.flatMap(_ => city) // nothing happens! runtime.unsafeRun(prg) //> Zdravo, Ljubljana!!!
  • 22. ZIO - 101 Program as values val salutation = console.putStr("Zdravo, ") val city = console.putStrLn("Ljubljana!!!") val prg = salutation *> city salutation.flatMap(_ => city) // nothing happens! runtime.unsafeRun(prg) //> Zdravo, Ljubljana!!!
  • 23. ZIO - 101 Program as values val salutation = console.putStr("Zdravo, ") val city = console.putStrLn("Ljubljana!!!") val prg = salutation *> city salutation.flatMap(_ => city) // nothing happens! runtime.unsafeRun(prg) //> Zdravo, Ljubljana!!!
  • 24. ZIO - 101 Program as values val salutation = console.putStr("Zdravo, ") val city = console.putStrLn("Ljubljana!!!") val prg = salutation *> city salutation.flatMap(_ => city) // nothing happens! runtime.unsafeRun(prg) //> Zdravo, Ljubljana!!!
  • 25. ZIO - 101 Program as values val salutation = console.putStr("Zdravo, ") val city = console.putStrLn("Ljubljana!!!") val prg = salutation *> city salutation.flatMap(_ => city) // nothing happens! runtime.unsafeRun(prg) //> Zdravo, Ljubljana!!!
  • 26. ZIO - 101 val prg = salutation *> city val prg: ZIO[Console, Nothing, Unit] = salutation *> city ZIO[-R, +E, +A] ⬇ R => IO[Either[E, A]] ⬇ R => Either[E, A]
  • 27. ZIO - 101 val prg = salutation *> city val prg: ZIO[Console, Nothing, Unit] = salutation *> city ZIO[-R, +E, +A] ⬇ R => IO[Either[E, A]] ⬇ R => Either[E, A]
  • 28. ZIO - 101 val prg = salutation *> city val prg: ZIO[Console, Nothing, Unit] = salutation *> city ZIO[-R, +E, +A] ⬇ R => IO[Either[E, A]] ⬇ R => Either[E, A]
  • 29. ZIO - 101 val prg = salutation *> city val prg: ZIO[Console, Nothing, Unit] = salutation *> city ZIO[-R, +E, +A] ⬇ R => IO[Either[E, A]] ⬇ R => Either[E, A]
  • 30. ZIO - 101 val prg: ZIO[Console, Nothing, Unit] = salutation *> city
  • 31. ZIO - 101 val prg: ZIO[Console, Nothing, Unit] = salutation *> city → Needs a Console to run
  • 32. ZIO - 101 val prg: ZIO[Console, Nothing, Unit] = salutation *> city → Needs a Console to run → Doesn't produce any error
  • 33. ZIO - 101 val prg: ZIO[Console, Nothing, Unit] = salutation *> city → Needs a Console to run → Doesn't produce any error → Produce () as output
  • 34. ZIO - 101 val prg: ZIO[Console, Nothing, Unit] = salutation *> city val miniRT = new Runtime[Any]{} // Provide console val provided = prg.provide(Console.Live) val provided: ZIO[Any, Nothing, Unit] = prg.provide(Console.Live) miniRT.unsafeRun(provided) //> Zdravo, Ljubljana!!! miniRT.unsafeRun(prg) // [error] found : zio.ZIO[zio.console.Console,Nothing,Unit] // [error] required: zio.ZIO[Any,?,?]
  • 35. ZIO - 101 val prg: ZIO[Console, Nothing, Unit] = salutation *> city val miniRT = new Runtime[Any]{} // Provide console val provided = prg.provide(Console.Live) val provided: ZIO[Any, Nothing, Unit] = prg.provide(Console.Live) miniRT.unsafeRun(provided) //> Zdravo, Ljubljana!!! miniRT.unsafeRun(prg) // [error] found : zio.ZIO[zio.console.Console,Nothing,Unit] // [error] required: zio.ZIO[Any,?,?]
  • 36. ZIO - 101 val prg: ZIO[Console, Nothing, Unit] = salutation *> city val miniRT = new Runtime[Any]{} // Provide console val provided = prg.provide(Console.Live) val provided: ZIO[Any, Nothing, Unit] = prg.provide(Console.Live) miniRT.unsafeRun(provided) //> Zdravo, Ljubljana!!! miniRT.unsafeRun(prg) // [error] found : zio.ZIO[zio.console.Console,Nothing,Unit] // [error] required: zio.ZIO[Any,?,?]
  • 37. ZIO - 101 val prg: ZIO[Console, Nothing, Unit] = salutation *> city val miniRT = new Runtime[Any]{} // Provide console val provided = prg.provide(Console.Live) val provided: ZIO[Any, Nothing, Unit] = prg.provide(Console.Live) miniRT.unsafeRun(provided) //> Zdravo, Ljubljana!!! miniRT.unsafeRun(prg) // [error] found : zio.ZIO[zio.console.Console,Nothing,Unit] // [error] required: zio.ZIO[Any,?,?]
  • 38. ZIO - 101 val prg: ZIO[Console, Nothing, Unit] = salutation *> city val miniRT = new Runtime[Any]{} // Provide console val provided = prg.provide(Console.Live) val provided: ZIO[Any, Nothing, Unit] = prg.provide(Console.Live) miniRT.unsafeRun(provided) //> Zdravo, Ljubljana!!! miniRT.unsafeRun(prg) // [error] found : zio.ZIO[zio.console.Console,Nothing,Unit] // [error] required: zio.ZIO[Any,?,?]
  • 39. ZIO - 101 val prg: ZIO[Console, Nothing, Unit] = salutation *> city val miniRT = new Runtime[Any]{} // Provide console val provided = prg.provide(Console.Live) val provided: ZIO[Any, Nothing, Unit] = prg.provide(Console.Live) miniRT.unsafeRun(provided) //> Zdravo, Ljubljana!!! miniRT.unsafeRun(prg) // [error] found : zio.ZIO[zio.console.Console,Nothing,Unit] // [error] required: zio.ZIO[Any,?,?]
  • 40. ZIO - 101 val prg: ZIO[Console, Nothing, Unit] = salutation *> city val miniRT = new Runtime[Any]{} // Provide console val provided = prg.provide(Console.Live) val provided: ZIO[Any, Nothing, Unit] = prg.provide(Console.Live) miniRT.unsafeRun(provided) //> Zdravo, Ljubljana!!! miniRT.unsafeRun(prg) // [error] found : zio.ZIO[zio.console.Console,Nothing,Unit] // [error] required: zio.ZIO[Any,?,?]
  • 41. ZIO - 101 Environment introduction/elimination // INTRODUCE AN ENVIRONMENT ZIO.access(f: R => A): ZIO[R, Nothing, A] ZIO.accessM(f: R => ZIO[R, E, A]): ZIO[R, E, A] // ELIMINATE AN ENVIRONMENT val prg: ZIO[Console, Nothing, Unit] prg.provide(Console.Live): ZIO[Any, Nothing, Unit]
  • 42. ZIO - 101 Environment introduction/elimination // INTRODUCE AN ENVIRONMENT ZIO.access(f: R => A): ZIO[R, Nothing, A] ZIO.accessM(f: R => ZIO[R, E, A]): ZIO[R, E, A] // ELIMINATE AN ENVIRONMENT val prg: ZIO[Console, Nothing, Unit] prg.provide(Console.Live): ZIO[Any, Nothing, Unit]
  • 43. ZIO - 101 Environment introduction/elimination // INTRODUCE AN ENVIRONMENT ZIO.access(f: R => A): ZIO[R, Nothing, A] ZIO.accessM(f: R => ZIO[R, E, A]): ZIO[R, E, A] // ELIMINATE AN ENVIRONMENT val prg: ZIO[Console, Nothing, Unit] prg.provide(Console.Live): ZIO[Any, Nothing, Unit]
  • 44. ZIO - 101 Environment introduction/elimination // INTRODUCE AN ENVIRONMENT ZIO.access(f: R => A): ZIO[R, Nothing, A] ZIO.accessM(f: R => ZIO[R, E, A]): ZIO[R, E, A] // ELIMINATE AN ENVIRONMENT val prg: ZIO[Console, Nothing, Unit] prg.provide(Console.Live): ZIO[Any, Nothing, Unit]
  • 45. ZIO - 101 Types at work type IO[+E, +A] = ZIO[Any, E, A] type UIO[+A] = ZIO[Any, Nothing, A]
  • 46. ZIO-101: Module Pattern Example: A Metrics module (or Algebra) // the module trait Metrics { val metrics: Metrics.Service[Any] } object Metrics { // the service trait Service[R] { def inc(label: String): ZIO[R, Nothing, Unit] } // the accessor object > extends Service[Metrics] { def inc(label: String): ZIO[Metrics, Nothing, Unit] = ZIO.accessM(_.metrics.inc(label)) } }
  • 47. ZIO-101: Module Pattern Example: A Metrics module (or Algebra) // the module trait Metrics { val metrics: Metrics.Service[Any] } object Metrics { // the service trait Service[R] { def inc(label: String): ZIO[R, Nothing, Unit] } // the accessor object > extends Service[Metrics] { def inc(label: String): ZIO[Metrics, Nothing, Unit] = ZIO.accessM(_.metrics.inc(label)) } }
  • 48. ZIO-101: Module Pattern Example: A Metrics module (or Algebra) // the module trait Metrics { val metrics: Metrics.Service[Any] } object Metrics { // the service trait Service[R] { def inc(label: String): ZIO[R, Nothing, Unit] } // the accessor object > extends Service[Metrics] { def inc(label: String): ZIO[Metrics, Nothing, Unit] = ZIO.accessM(_.metrics.inc(label)) } }
  • 49. ZIO-101: Module Pattern A Metrics module - Running val prg2: ZIO[Metrics with Log, Nothing, Unit] = for { _ <- Log.>.info("Hello") _ <- Metrics.>.inc("salutation") _ <- Log.>.info("BeeScala") _ <- Metrics.>.inc("subject") } yield () trait Prometheus extends Metrics { val metrics = new Metrics.Service[Any] { def inc(label: String): ZIO[Any, Nothing, Unit] = ZIO.effectTotal(counter.labels(label).inc(1)) } } miniRT.unsafeRun( prg2.provide(new Prometheus with Log.Live) )
  • 50. ZIO-101: Module Pattern A Metrics module - Running val prg2: ZIO[Metrics with Log, Nothing, Unit] = for { _ <- Log.>.info("Hello") _ <- Metrics.>.inc("salutation") _ <- Log.>.info("BeeScala") _ <- Metrics.>.inc("subject") } yield () trait Prometheus extends Metrics { val metrics = new Metrics.Service[Any] { def inc(label: String): ZIO[Any, Nothing, Unit] = ZIO.effectTotal(counter.labels(label).inc(1)) } } miniRT.unsafeRun( prg2.provide(new Prometheus with Log.Live) )
  • 51. ZIO-101: Module Pattern A Metrics module - Running val prg2: ZIO[Metrics with Log, Nothing, Unit] = for { _ <- Log.>.info("Hello") _ <- Metrics.>.inc("salutation") _ <- Log.>.info("BeeScala") _ <- Metrics.>.inc("subject") } yield () trait Prometheus extends Metrics { val metrics = new Metrics.Service[Any] { def inc(label: String): ZIO[Any, Nothing, Unit] = ZIO.effectTotal(counter.labels(label).inc(1)) } } miniRT.unsafeRun( prg2.provide(new Prometheus with Log.Live) )
  • 52. ZIO-101: Module Pattern A Metrics module - Running val prg2: ZIO[Metrics with Log, Nothing, Unit] = for { _ <- Log.>.info("Hello") _ <- Metrics.>.inc("salutation") _ <- Log.>.info("BeeScala") _ <- Metrics.>.inc("subject") } yield () trait Prometheus extends Metrics { val metrics = new Metrics.Service[Any] { def inc(label: String): ZIO[Any, Nothing, Unit] = ZIO.effectTotal(counter.labels(label).inc(1)) } } miniRT.unsafeRun( prg2.provide(new Prometheus with Log.Live) )
  • 53. ZIO-101: Module Pattern A Metrics module - Running val prg2: ZIO[Metrics with Log, Nothing, Unit] = for { _ <- Log.>.info("Hello") _ <- Metrics.>.inc("salutation") _ <- Log.>.info("BeeScala") _ <- Metrics.>.inc("subject") } yield () trait Prometheus extends Metrics { val metrics = new Metrics.Service[Any] { def inc(label: String): ZIO[Any, Nothing, Unit] = ZIO.effectTotal(counter.labels(label).inc(1)) } } miniRT.unsafeRun( prg2.provide(new Prometheus with Log.Live) )
  • 54. ZIO-101: Module Pattern A Metrics module - testing val prg2: ZIO[Metrics with Log, Nothing, Unit] = /* ... */ case class TestMetrics(incCalls: Ref[List[String]]) extends Metrics.Service[Any] { def inc(label: String): ZIO[Any, Nothing, Unit] = incCalls.update(xs => xs :+ label).unit } val test = for { ref <- Ref.make(List[String]()) _ <- prg2.provide(new Log.Live with Metrics { val metrics = TestMetrics(ref) }) calls <- ref.get _ <- UIO.effectTotal(assert(calls == List("salutation", "subject"))) } yield () miniRt.unsafeRun(test)
  • 55. ZIO-101: Module Pattern A Metrics module - testing val prg2: ZIO[Metrics with Log, Nothing, Unit] = /* ... */ case class TestMetrics(incCalls: Ref[List[String]]) extends Metrics.Service[Any] { def inc(label: String): ZIO[Any, Nothing, Unit] = incCalls.update(xs => xs :+ label).unit } val test = for { ref <- Ref.make(List[String]()) _ <- prg2.provide(new Log.Live with Metrics { val metrics = TestMetrics(ref) }) calls <- ref.get _ <- UIO.effectTotal(assert(calls == List("salutation", "subject"))) } yield () miniRt.unsafeRun(test)
  • 56. ZIO-101: Module Pattern A Metrics module - testing val prg2: ZIO[Metrics with Log, Nothing, Unit] = /* ... */ case class TestMetrics(incCalls: Ref[List[String]]) extends Metrics.Service[Any] { def inc(label: String): ZIO[Any, Nothing, Unit] = incCalls.update(xs => xs :+ label).unit } val test = for { ref <- Ref.make(List[String]()) _ <- prg2.provide(new Log.Live with Metrics { val metrics = TestMetrics(ref) }) calls <- ref.get _ <- UIO.effectTotal(assert(calls == List("salutation", "subject"))) } yield () miniRt.unsafeRun(test)
  • 57. ZIO-101: Module Pattern A Metrics module - testing val prg2: ZIO[Metrics with Log, Nothing, Unit] = /* ... */ case class TestMetrics(incCalls: Ref[List[String]]) extends Metrics.Service[Any] { def inc(label: String): ZIO[Any, Nothing, Unit] = incCalls.update(xs => xs :+ label).unit } val test = for { ref <- Ref.make(List[String]()) _ <- prg2.provide(new Log.Live with Metrics { val metrics = TestMetrics(ref) }) calls <- ref.get _ <- UIO.effectTotal(assert(calls == List("salutation", "subject"))) } yield () miniRt.unsafeRun(test)
  • 58. ZIO-101: Module Pattern A Metrics module - testing val prg2: ZIO[Metrics with Log, Nothing, Unit] = /* ... */ case class TestMetrics(incCalls: Ref[List[String]]) extends Metrics.Service[Any] { def inc(label: String): ZIO[Any, Nothing, Unit] = incCalls.update(xs => xs :+ label).unit } val test = for { ref <- Ref.make(List[String]()) _ <- prg2.provide(new Log.Live with Metrics { val metrics = TestMetrics(ref) }) calls <- ref.get _ <- UIO.effectTotal(assert(calls == List("salutation", "subject"))) } yield () miniRt.unsafeRun(test)
  • 59. ZIO-101: Module Pattern A Metrics module - testing val prg2: ZIO[Metrics with Log, Nothing, Unit] = /* ... */ case class TestMetrics(incCalls: Ref[List[String]]) extends Metrics.Service[Any] { def inc(label: String): ZIO[Any, Nothing, Unit] = incCalls.update(xs => xs :+ label).unit } val test = for { ref <- Ref.make(List[String]()) _ <- prg2.provide(new Log.Live with Metrics { val metrics = TestMetrics(ref) }) calls <- ref.get _ <- UIO.effectTotal(assert(calls == List("salutation", "subject"))) } yield () miniRt.unsafeRun(test)
  • 60. ZIO-101: Module Pattern A Metrics module - testing val prg2: ZIO[Metrics with Log, Nothing, Unit] = /* ... */ case class TestMetrics(incCalls: Ref[List[String]]) extends Metrics.Service[Any] { def inc(label: String): ZIO[Any, Nothing, Unit] = incCalls.update(xs => xs :+ label).unit } val test = for { ref <- Ref.make(List[String]()) _ <- prg2.provide(new Log.Live with Metrics { val metrics = TestMetrics(ref) }) calls <- ref.get _ <- UIO.effectTotal(assert(calls == List("salutation", "subject"))) } yield () miniRt.unsafeRun(test)
  • 63. Ray tracing → World (spheres), light source, camera
  • 64. Ray tracing → World (spheres), light source, camera → Incident rays
  • 65. Ray tracing → World (spheres), light source, camera → Incident rays → Reflected rays
  • 66. Ray tracing → World (spheres), light source, camera → Incident rays → Reflected rays → Discarded rays → Canvas
  • 67. Ray tracing → World (spheres), light source, camera → Incident rays → Reflected rays → Discarded rays → Canvas → Colored pixels
  • 68. Ray tracing → World (spheres), light source, camera → Incident rays → Reflected rays → Discarded rays → Canvas → Colored pixels
  • 70. Ray tracing Options: 1. Compute all the rays (and discard most of them)
  • 71. Ray tracing Options: 1. Compute all the rays (and discard most of them) 2. Compute only the rays outgoing from the camera through the canvas, and determine how they behave on the surfaces
  • 72. Ray A ray is defined by the point it starts from, and its direction
  • 75. Foundations → Points and Vectors → Transformations (rotate, scale, translate)
  • 76. Points and Vectors case class Vec(x: Double, y: Double, z: Double) { def +(other: Vec): Vec = Vec(x + other.x, y + other.y, z + other.z) def unary_- : Vec = Vec(-x, -y, -z) } case class Pt(x: Double, y: Double, z: Double) { def -(otherPt: Pt): Vec = Vec(x - otherPt.x, y - otherPt.y, z - otherPt.z) def +(vec: Vec) = Pt(x + vec.x, y + vec.y, z + vec.z) }
  • 77. Points and Vectors case class Vec(x: Double, y: Double, z: Double) { def +(other: Vec): Vec = Vec(x + other.x, y + other.y, z + other.z) def unary_- : Vec = Vec(-x, -y, -z) } case class Pt(x: Double, y: Double, z: Double) { def -(otherPt: Pt): Vec = Vec(x - otherPt.x, y - otherPt.y, z - otherPt.z) def +(vec: Vec) = Pt(x + vec.x, y + vec.y, z + vec.z) }
  • 78. Points and Vectors zio-test for PBT testM("vectors form a group")( check(vecGen, vecGen, vecGen) { (v1, v2, v3) => assertApprox (v1 + (v2 + v3), (v1 + v2) + v3) && assertApprox (v1 + v2 , v2 + v1) && assertApprox (v1 + Vec.zero , Vec.zero + v1) } ), testM("vectors and points form an affine space") ( check(ptGen, ptGen) { (p1, p2) => assertApprox (p2, p1 + (p2 - p1)) } )
  • 79. Points and Vectors zio-test for PBT testM("vectors form a group")( check(vecGen, vecGen, vecGen) { (v1, v2, v3) => assertApprox (v1 + (v2 + v3), (v1 + v2) + v3) && assertApprox (v1 + v2 , v2 + v1) && assertApprox (v1 + Vec.zero , Vec.zero + v1) } ), testM("vectors and points form an affine space") ( check(ptGen, ptGen) { (p1, p2) => assertApprox (p2, p1 + (p2 - p1)) } )
  • 80. Ray case class Ray(origin: Pt, direction: Vec) { def positionAt(t: Double): Pt = origin + (direction * t) }
  • 81. Transformations trait AT /* Module */ trait ATModule { val aTModule: ATModule.Service[Any] } object ATModule { /* Service */ trait Service[R] { def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec] def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt] def compose(first: AT, second: AT): ZIO[R, ATError, AT] } /* Accessor */ object > extends Service[ATModule] { def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] = ZIO.accessM(_.aTModule.applyTf(tf, vec)) def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] = ZIO.accessM(_.aTModule.applyTf(tf, pt)) def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] = ZIO.accessM(_.aTModule.compose(first, second)) } }
  • 82. Transformations trait AT /* Module */ trait ATModule { val aTModule: ATModule.Service[Any] } object ATModule { /* Service */ trait Service[R] { def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec] def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt] def compose(first: AT, second: AT): ZIO[R, ATError, AT] } /* Accessor */ object > extends Service[ATModule] { def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] = ZIO.accessM(_.aTModule.applyTf(tf, vec)) def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] = ZIO.accessM(_.aTModule.applyTf(tf, pt)) def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] = ZIO.accessM(_.aTModule.compose(first, second)) } }
  • 83. Transformations trait AT /* Module */ trait ATModule { val aTModule: ATModule.Service[Any] } object ATModule { /* Service */ trait Service[R] { def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec] def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt] def compose(first: AT, second: AT): ZIO[R, ATError, AT] } /* Accessor */ object > extends Service[ATModule] { def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] = ZIO.accessM(_.aTModule.applyTf(tf, vec)) def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] = ZIO.accessM(_.aTModule.applyTf(tf, pt)) def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] = ZIO.accessM(_.aTModule.compose(first, second)) } }
  • 84. Transformations trait AT /* Module */ trait ATModule { val aTModule: ATModule.Service[Any] } object ATModule { /* Service */ trait Service[R] { def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec] def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt] def compose(first: AT, second: AT): ZIO[R, ATError, AT] } /* Accessor */ object > extends Service[ATModule] { def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] = ZIO.accessM(_.aTModule.applyTf(tf, vec)) def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] = ZIO.accessM(_.aTModule.applyTf(tf, pt)) def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] = ZIO.accessM(_.aTModule.compose(first, second)) } }
  • 85. Transformations import zio.macros.annotation.accessible trait AT /* Module */ @accessible(">") trait ATModule { val aTModule: ATModule.Service[Any] } object ATModule { /* Service */ trait Service[R] { def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec] def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt] def compose(first: AT, second: AT): ZIO[R, ATError, AT] } /* Accessor is generated object > extends Service[ATModule] { def applyTf(tf: AT, vec: Vec): ZIO[ATModule, ATError, Vec] = ZIO.accessM(_.aTModule.applyTf(tf, vec)) def applyTf(tf: AT, pt: Pt): ZIO[ATModule, ATError, Pt] = ZIO.accessM(_.aTModule.applyTf(tf, pt)) def compose(first: AT, second: AT): ZIO[ATModule, ATError, AT] = ZIO.accessM(_.aTModule.compose(first, second)) } */ }
  • 86. Transformations trait AT /* Module */ @accessible(">") trait ATModule { val aTModule: ATModule.Service[Any] } object ATModule { /* Service */ trait Service[R] { def applyTf(tf: AT, vec: Vec): ZIO[R, ATError, Vec] def applyTf(tf: AT, pt: Pt): ZIO[R, ATError, Pt] def compose(first: AT, second: AT): ZIO[R, ATError, AT] } }
  • 87. Transformations val rotatedPt = for { rotateX <- ATModule.>.rotateX(math.Pi / 2) _ <- Log.>.info("rotated of π/2") res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1)) } yield res
  • 88. Transformations val rotatedPt: ZIO[ATModule with Log, ATError, Pt] = for { rotateX <- ATModule.>.rotateX(math.Pi / 2) _ <- Log.>.info("rotated of π/2") res <- ATModule.>.applyTf(rotateX, Pt(1, 1, 1)) } yield res
  • 89. Transformations - Live val rotated: ZIO[ATModule, ATError, Vec] = for { rotateX <- ATModule.>.rotateZ(math.Pi/2) res <- ATModule.>.applyTf(rotateX, Vec(x, y, z)) } yield res
  • 90. Transformations - Live val rotated: ZIO[ATModule, ATError, Vec] = for { rotateX <- ATModule.>.rotateZ(math.Pi/2) res <- ATModule.>.applyTf(rotateX, Vec(x, y, z)) } yield res → Vec(x, y, z)
  • 91. Transformations - Live val rotated: ZIO[ATModule, ATError, Vec] = for { rotateX <- ATModule.>.rotateZ(math.Pi/2) res <- ATModule.>.applyTf(rotateX, Vec(x, y, z)) } yield res → Vec(x, y, z) → Pt(x, y, z)⠀
  • 92. Transformations - Live val rotated: ZIO[ATModule, ATError, Vec] = for { rotateX <- ATModule.>.rotateZ(math.Pi/2) res <- ATModule.>.applyTf(rotateX, Vec(x, y, z)) } yield res → Vec(x, y, z) → Pt(x, y, z)⠀ → ⠀
  • 93. Transformations - Live // Defined somewhere else object MatrixModule { trait Service[R] { def add(m1: M, m2: M): ZIO[R, AlgebraicError, M] def mul(m1: M, m2: M): ZIO[R, AlgebraicError, M] } } trait Live extends ATModule { val matrixModule: MatrixModule.Service[Any] val aTModule: ATModule.Service[Any] = new ATModule.Service[Any] { def applyTf(tf: AT, vec: Vec): ZIO[Any, AlgebraicError, Vec] = for { col <- PointVec.toCol(vec) colRes <- matrixModule.mul(tf, col) res <- PointVec.colToVec(colRes) } yield res
  • 94. Transformations - Live // Defined somewhere else object MatrixModule { trait Service[R] { def add(m1: M, m2: M): ZIO[R, AlgebraicError, M] def mul(m1: M, m2: M): ZIO[R, AlgebraicError, M] } } trait Live extends ATModule { val matrixModule: MatrixModule.Service[Any] val aTModule: ATModule.Service[Any] = new ATModule.Service[Any] { def applyTf(tf: AT, vec: Vec): ZIO[Any, AlgebraicError, Vec] = for { col <- PointVec.toCol(vec) colRes <- matrixModule.mul(tf, col) res <- PointVec.colToVec(colRes) } yield res
  • 95. Transformations - running val rotated: ZIO[ATModule, ATError, Vec] = ... val program = rotatedPt.provide(new ATModule.Live{}) // Compiler error: // object creation impossible, since value matrixModule // in trait Live of t ype matrix.MatrixModule.Service[Any] is not defined // [error] rotatedPt.provide(new ATModule.Live{})
  • 96. Transformations - running val rotated: ZIO[ATModule, ATError, Vec] = ... val program = rotatedPt.provide(new ATModule.Live{}) // Compiler error: // object creation impossible, since value matrixModule // in trait Live of t ype matrix.MatrixModule.Service[Any] is not defined // [error] rotatedPt.provide(new ATModule.Live{})
  • 97. Transformations - running val rotated: ZIO[ATModule, ATError, Vec] = ... val program = rotatedPt.provide(new ATModule.Live{}) // Compiler error: // object creation impossible, since value matrixModule // in trait Live of t ype matrix.MatrixModule.Service[Any] is not defined // [error] rotatedPt.provide(new ATModule.Live{})
  • 98. Transformations - running val rotated: ZIO[ATModule, ATError, Vec] = ... val program = rotatedPt.provide( new ATModule.Live with MatrixModule.BreezeLive ) // Compiles! runtime.unsafeRun(program) // Runs!
  • 99. Transformations - running val rotated: ZIO[ATModule, ATError, Vec] = ... val program = rotatedPt.provide( new ATModule.Live with MatrixModule.BreezeLive ) // Compiles! runtime.unsafeRun(program) // Runs!
  • 100. Transformations - running val rotated: ZIO[ATModule, ATError, Vec] = ... val program = rotatedPt.provide( new ATModule.Live with MatrixModule.BreezeLive ) // Compiles! runtime.unsafeRun(program) // Runs!
  • 103. Camera Everything is relative! → Canonical camera: observe always from x = 0 and translate the world by +3
  • 104. Camera - canonical case class Camera ( hRes: Int, vRes: Int, fieldOfViewRad: Double, tf: AT )
  • 105. Camera - generic object Camera { def worldTransformation(from: Pt, to: Pt, up: Vec): ZIO[ATModule, Nothing, AT] = for { orientationAT <- // some prepration ... translateTf <- ATModule.>.translate(-from.x, -from.y, -from.z) composed <- ATModule.>.compose(translateTf, orientationAT) } yield composed def make( viewFrom: Pt, viewTo: Pt, upDirection: Vec, visualAngleRad: Double, hRes: Int, vRes: Int): ZIO[ATModule, AlgebraicError, Camera] = worldTransformation(viewFrom, viewTo, upDirection).map { worldTf => Camera(hRes, vRes, visualAngleRad, worlfTf) }
  • 106. Camera - generic object Camera { def worldTransformation(from: Pt, to: Pt, up: Vec): ZIO[ATModule, Nothing, AT] = for { orientationAT <- // some prepration ... translateTf <- ATModule.>.translate(-from.x, -from.y, -from.z) composed <- ATModule.>.compose(translateTf, orientationAT) } yield composed def make( viewFrom: Pt, viewTo: Pt, upDirection: Vec, visualAngleRad: Double, hRes: Int, vRes: Int): ZIO[ATModule, AlgebraicError, Camera] = worldTransformation(viewFrom, viewTo, upDirection).map { worldTf => Camera(hRes, vRes, visualAngleRad, worlfTf) }
  • 107. Camera - generic object Camera { def worldTransformation(from: Pt, to: Pt, up: Vec): ZIO[ATModule, Nothing, AT] = for { orientationAT <- // some prepration ... translateTf <- ATModule.>.translate(-from.x, -from.y, -from.z) composed <- ATModule.>.compose(translateTf, orientationAT) } yield composed def make( viewFrom: Pt, viewTo: Pt, upDirection: Vec, visualAngleRad: Double, hRes: Int, vRes: Int): ZIO[ATModule, AlgebraicError, Camera] = worldTransformation(viewFrom, viewTo, upDirection).map { worldTf => Camera(hRes, vRes, visualAngleRad, worlfTf) }
  • 108. Camera - generic object Camera { def worldTransformation(from: Pt, to: Pt, up: Vec): ZIO[ATModule, Nothing, AT] = for { orientationAT <- // some prepration ... translateTf <- ATModule.>.translate(-from.x, -from.y, -from.z) composed <- ATModule.>.compose(translateTf, orientationAT) } yield composed def make( viewFrom: Pt, viewTo: Pt, upDirection: Vec, visualAngleRad: Double, hRes: Int, vRes: Int): ZIO[ATModule, AlgebraicError, Camera] = worldTransformation(viewFrom, viewTo, upDirection).map { worldTf => Camera(hRes, vRes, visualAngleRad, worlfTf) }
  • 109. World sealed trait Shape { def transformation: AT def material: Material } case class Sphere(transformation: AT, material: Material) extends Shape case class Plane(transformation: AT, material: Material) extends Shape
  • 110. World → Sphere.canonical sealed trait Shape { def transformation: AT def material: Material } case class Sphere(transformation: AT, material: Material) extends Shape case class Plane(transformation: AT, material: Material) extends Shape
  • 111. World → Sphere.canonical → Plane.canonical sealed trait Shape { def transformation: AT def material: Material } case class Sphere(transformation: AT, material: Material) extends Shape case class Plane(transformation: AT, material: Material) extends Shape
  • 112. World → Sphere.canonical → Plane.canonical sealed trait Shape { def transformation: AT def material: Material } case class Sphere(transformation: AT, material: Material) extends Shape case class Plane(transformation: AT, material: Material) extends Shape
  • 113. World → Sphere.canonical → Plane.canonical sealed trait Shape { def transformation: AT def material: Material } case class Sphere(transformation: AT, material: Material) extends Shape case class Plane(transformation: AT, material: Material) extends Shape
  • 114. World → Sphere.canonical → Plane.canonical sealed trait Shape { def transformation: AT def material: Material } case class Sphere(transformation: AT, material: Material) extends Shape case class Plane(transformation: AT, material: Material) extends Shape
  • 115. World → Sphere.canonical → Plane.canonical sealed trait Shape { def transformation: AT def material: Material } case class Sphere(transformation: AT, material: Material) extends Shape case class Plane(transformation: AT, material: Material) extends Shape
  • 116. World Make a world object Sphere { def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for { scale <- ATModule.>.scale(radius, radius, radius) translate <- ATModule.>.translate(center.x, center.y, center.z) composed <- ATModule.>.compose(scale, translate) } yield Sphere(composed, mat) } object Plane { def make(...): ZIO[ATModule, ATError, Plane] = ??? } case class World(pointLight: PointLight, objects: List[Shape])
  • 117. World Make a world object Sphere { def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for { scale <- ATModule.>.scale(radius, radius, radius) translate <- ATModule.>.translate(center.x, center.y, center.z) composed <- ATModule.>.compose(scale, translate) } yield Sphere(composed, mat) } object Plane { def make(...): ZIO[ATModule, ATError, Plane] = ??? } case class World(pointLight: PointLight, objects: List[Shape])
  • 118. World Make a world object Sphere { def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for { scale <- ATModule.>.scale(radius, radius, radius) translate <- ATModule.>.translate(center.x, center.y, center.z) composed <- ATModule.>.compose(scale, translate) } yield Sphere(composed, mat) } object Plane { def make(...): ZIO[ATModule, ATError, Plane] = ??? } case class World(pointLight: PointLight, objects: List[Shape])
  • 119. World Make a world object Sphere { def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for { scale <- ATModule.>.scale(radius, radius, radius) translate <- ATModule.>.translate(center.x, center.y, center.z) composed <- ATModule.>.compose(scale, translate) } yield Sphere(composed, mat) } object Plane { def make(...): ZIO[ATModule, ATError, Plane] = ??? } case class World(pointLight: PointLight, objects: List[Shape])
  • 120. World Make a world object Sphere { def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for { scale <- ATModule.>.scale(radius, radius, radius) translate <- ATModule.>.translate(center.x, center.y, center.z) composed <- ATModule.>.compose(scale, translate) } yield Sphere(composed, mat) } object Plane { def make(...): ZIO[ATModule, ATError, Plane] = ??? } case class World(pointLight: PointLight, objects: List[Shape])
  • 121. World Make a world object Sphere { def make(center: Pt, radius: Double, mat: Material): ZIO[ATModule, ATError, Sphere] = for { scale <- ATModule.>.scale(radius, radius, radius) translate <- ATModule.>.translate(center.x, center.y, center.z) composed <- ATModule.>.compose(scale, translate) } yield Sphere(composed, mat) } object Plane { def make(...): ZIO[ATModule, ATError, Plane] = ??? } case class World(pointLight: PointLight, objects: List[Shape]) → Everything requires ATModule
  • 122. World Rendering - Top Down Rastering - Generate a stream of colored pixels @accessible(">") trait RasteringModule { val rasteringModule: RasteringModule.Service[Any] } object RasteringModule { trait Service[R] { def raster(world: World, camera: Camera): ZStream[R, RayTracerError, ColoredPixel] } trait AllWhiteTestRasteringModule extends RasteringModule { val rasteringModule: Service[Any] = new Service[Any] { def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] = for { x <- ZStream.fromIterable(0 until camera.hRes) y <- ZStream.fromIterable(0 until camera.vRes) } yield ColoredPixel(Pixel(x, y), Color.white)) }
  • 123. World Rendering - Top Down Rastering - Generate a stream of colored pixels @accessible(">") trait RasteringModule { val rasteringModule: RasteringModule.Service[Any] } object RasteringModule { trait Service[R] { def raster(world: World, camera: Camera): ZStream[R, RayTracerError, ColoredPixel] } trait AllWhiteTestRasteringModule extends RasteringModule { val rasteringModule: Service[Any] = new Service[Any] { def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] = for { x <- ZStream.fromIterable(0 until camera.hRes) y <- ZStream.fromIterable(0 until camera.vRes) } yield ColoredPixel(Pixel(x, y), Color.white)) }
  • 124. World Rendering - Top Down Rastering - Generate a stream of colored pixels @accessible(">") trait RasteringModule { val rasteringModule: RasteringModule.Service[Any] } object RasteringModule { trait Service[R] { def raster(world: World, camera: Camera): ZStream[R, RayTracerError, ColoredPixel] } trait AllWhiteTestRasteringModule extends RasteringModule { val rasteringModule: Service[Any] = new Service[Any] { def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] = for { x <- ZStream.fromIterable(0 until camera.hRes) y <- ZStream.fromIterable(0 until camera.vRes) } yield ColoredPixel(Pixel(x, y), Color.white)) }
  • 125. World Rendering - Top Down Rastering - Live object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 126. World Rendering - Top Down Rastering - Live → Camera module - Ray per pixel object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 127. World Rendering - Top Down Rastering - Live → Camera module - Ray per pixel object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 128. World Rendering - Top Down Rastering - Live → Camera module - Ray per pixel object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 129. World Rendering - Top Down Rastering - Live → Camera module - Ray per pixel object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 130. World Rendering - Top Down Rastering - Live → Camera module - Ray per pixel object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } → World module - Color per ray object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 131. World Rendering - Top Down Rastering - Live → Camera module - Ray per pixel object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } → World module - Color per ray object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 132. World Rendering - Top Down Rastering - Live → Camera module - Ray per pixel object CameraModule { trait Service[R] { def rayForPixel( camera: Camera, px: Int, py: Int ): ZIO[R, Nothing, Ray] } } → World module - Color per ray object WorldModule { trait Service[R] { def colorForRay( world: World, ray: Ray ): ZIO[R, RayTracerError, Color] } }
  • 133. World Rendering - Top Down Rastering - Live trait LiveRasteringModule extends RasteringModule { val cameraModule: CameraModule.Service[Any] val worldModule: WorldModule.Service[Any] val rasteringModule: Service[Any] = new Service[Any] { def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] = { val pixels: Stream[Nothing, (Int, Int)] = ??? pixels.mapM{ case (px, py) => for { ray <- cameraModule.rayForPixel(camera, px, py) color <- worldModule.colorForRay(world, ray) } yield data.ColoredPixel(Pixel(px, py), color) } } } }
  • 134. World Rendering - Top Down Rastering - Live trait LiveRasteringModule extends RasteringModule { val cameraModule: CameraModule.Service[Any] val worldModule: WorldModule.Service[Any] val rasteringModule: Service[Any] = new Service[Any] { def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] = { val pixels: Stream[Nothing, (Int, Int)] = ??? pixels.mapM{ case (px, py) => for { ray <- cameraModule.rayForPixel(camera, px, py) color <- worldModule.colorForRay(world, ray) } yield data.ColoredPixel(Pixel(px, py), color) } } } }
  • 135. World Rendering - Top Down Rastering - Live trait LiveRasteringModule extends RasteringModule { val cameraModule: CameraModule.Service[Any] val worldModule: WorldModule.Service[Any] val rasteringModule: Service[Any] = new Service[Any] { def raster(world: World, camera: Camera): ZStream[Any, RayTracerError, ColoredPixel] = { val pixels: Stream[Nothing, (Int, Int)] = ??? pixels.mapM{ case (px, py) => for { ray <- cameraModule.rayForPixel(camera, px, py) color <- worldModule.colorForRay(world, ray) } yield data.ColoredPixel(Pixel(px, py), color) } } } }
  • 136. Test LiveRasteringModule 1 - Define the method under test val world = /* prepare a world */ val camera = /* prepare a camera */ val appUnderTest: ZIO[RasteringModule, RayTracerError, List[ColoredPixel]] = RasteringModule.>.raster(world, camera) .flatMap(_.runCollect)
  • 137. Test LiveRasteringModule 2 - Annotate the modules as mockable import zio.macros.annotation.mockable @mockable trait CameraModule { ... } @mockable trait WorldModule { ... }
  • 138. Test LiveRasteringModule 3 - Build the expectations val rayForPixelExp: Expectation[CameraModule, Nothing, Ray] = (CameraModule.rayForPixel(equalTo((camera, 0, 0))) returns value(r1)) *> (CameraModule.rayForPixel(equalTo((camera, 0, 1))) returns value(r2)) val colorForRayExp: Expectation[WorldModule, Nothing, Color] = (WorldModule.colorForRay(equalTo((world, r1, 5))) returns value(Color.red)) *> (WorldModule.colorForRay(equalTo((world, r2, 5))) returns value(Color.green))
  • 139. Test LiveRasteringModule 4 - Build the environment for the code under test val appUnderTest: ZIO[RasteringModule, RayTracerError, List[ColoredPixel]] = RasteringModule.>.raster(world, camera) .flatMap(_.runCollect) appUnderTest.provideManaged( worldModuleExp.managedEnv.zipWith(cameraModuleExp.managedEnv) { (wm, cm) => new LiveRasteringModule { val cameraModule: CameraModule.Service[Any] = cm.cameraModule val worldModule: WorldModule.Service[Any] = wm.worldModule } } )
  • 140. Test LiveRasteringModule 4 - Build the environment for the code under test val appUnderTest: ZIO[RasteringModule, RayTracerError, List[ColoredPixel]] = RasteringModule.>.raster(world, camera) .flatMap(_.runCollect) appUnderTest.provideManaged( worldModuleExp.managedEnv.zipWith(cameraModuleExp.managedEnv) { (wm, cm) => new LiveRasteringModule { val cameraModule: CameraModule.Service[Any] = cm.cameraModule val worldModule: WorldModule.Service[Any] = wm.worldModule } } )
  • 141. Test LiveRasteringModule 5 - Assert on the results assert(res, equalTo(List( ColoredPixel(Pixel(0, 0), Color.red), ColoredPixel(Pixel(0, 1), Color.green), ColoredPixel(Pixel(1, 0), Color.blue), ColoredPixel(Pixel(1, 1), Color.white), )) )
  • 142. Test LiveRasteringModule suite("LiveRasteringModule") { testM("raster should rely on cameraModule and world module") { val camera = Camera.makeUnsafe(Pt.origin, Pt(0, 0, -1), Vec.uy, math.Pi / 3, 2, 2) val world = World(PointLight(Pt(5, 5, 5), Color.white), List()) val appUnderTest: ZIO[RasteringModule, RayTracerError, List[ColoredPixel]] = RasteringModule.>.raster(world, camera).flatMap(_.runCollect) for { (worldModuleExp, cameraModuleExp) <- RasteringModuleMocks.mockExpectations(world, camera) res <- appUnderTest.provideManaged( worldModuleExp.managedEnv.zipWith(cameraModuleExp.managedEnv) { (wm, cm) => new LiveRasteringModule { val cameraModule: CameraModule.Service[Any] = cm.cameraModule val worldModule: WorldModule.Service[Any] = wm.worldModule } } ) } yield assert(res, equalTo(List( ColoredPixel(Pixel(0, 0), Color.red), ColoredPixel(Pixel(0, 1), Color.green) ))) } }
  • 143. Test Takeaway: Implement and test every layer only in terms of the immediately underlying layer
  • 144.
  • 145. Live CameraModule trait Live extends CameraModule { val aTModule: ATModule.Service[Any] /* implementation */ }
  • 146. Live WorldModule WorldTopologyModule trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay( world: World, ray: Ray, remaining: Ref[Int] ): ZIO[Any, RayTracerError, Color] = { /* use other modules */ } } }
  • 147. Live WorldModule WorldTopologyModule trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay( world: World, ray: Ray, remaining: Ref[Int] ): ZIO[Any, RayTracerError, Color] = { /* use other modules */ } } }
  • 148. Live WorldModule WorldHitCompsModule trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldHitCompsModule: WorldHitCompsModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay( world: World, ray: Ray, remaining: Ref[Int] ): ZIO[Any, RayTracerError, Color] = { /* use other modules */ } } }
  • 149. Live WorldModule WorldHitCompsModule case class HitComps( shape: Shape, hitPt: Pt, normalV: Vec, eyeV: Vec, rayReflectV: Vec ) trait Service[R] { def hitComps( ray: Ray, hit: Intersection, intersections: List[Intersection] ): ZIO[R, Nothing, HitComps] }
  • 150. Live WorldModule PhongReflectionModule trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldHitCompsModule: WorldHitCompsModule.Service[Any] val phongReflectionModule: PhongReflectionModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay( world: World, ray: Ray, remaining: Ref[Int] ): ZIO[Any, RayTracerError, Color] = { /* use other modules */ } } }
  • 151. Live WorldModule PhongReflectionModule case class PhongComponents( ambient: Color, diffuse: Color, reflective: Color ) { def toColor: Color = ambient + diffuse + reflective } trait BlackWhite extends PhongReflectionModule { val phongReflectionModule: Service[Any] = new Service[Any] { def lighting( pointLight: PointLight, hitComps: HitComps, inShadow: Boolean ): UIO[PhongComponents] = { if (inShadow) UIO(PhongComponents.allBlack) else UIO(PhongComponents.allWhite) } } }
  • 152. Live WorldModule PhongReflectionModule case class PhongComponents( ambient: Color, diffuse: Color, reflective: Color ) { def toColor: Color = ambient + diffuse + reflective } trait BlackWhite extends PhongReflectionModule { val phongReflectionModule: Service[Any] = new Service[Any] { def lighting( pointLight: PointLight, hitComps: HitComps, inShadow: Boolean ): UIO[PhongComponents] = { if (inShadow) UIO(PhongComponents.allBlack) else UIO(PhongComponents.allWhite) } } }
  • 153. Display the first canvas / 1 def drawOnCanvasWithCamera(world: World, camera: Camera, canvas: Canvas): ZIO[RasteringModule, RayTracerError, Unit] = for { coloredPointsStream <- RasteringModule.>.raster(world, camera) _ <- coloredPointsStream.mapM(cp => canvas.update(cp)).run(Sink.drain) } yield () def program(viewFrom: Pt): ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit] = for { camera <- cameraFor(viewFrom: Pt) w <- world canvas <- Canvas.create() _ <- drawOnCanvasWithCamera(w, camera, canvas) _ <- CanvasSerializer.>.serialize(canvas, 255) } yield ()
  • 154. Display the first canvas / 2 def program(viewFrom: Pt): ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit] program(Pt(2, 2, -10)) .provide( new CanvasSerializer.PPMCanvasSerializer with RasteringModule.ChunkRasteringModule with ATModule.Live ) // Members declared in zio.blocking.Blocking // [error] val blocking: zio.blocking.Blocking.Service[Any] = ??? // [error] // [error] // Members declared in modules.RasteringModule.ChunkRasteringModule // [error] val cameraModule: modules.CameraModule.Service[Any] = ??? // [error] val worldModule: modules.WorldModule.Service[Any] = ??? // [error] // [error] // Members declared in geometry.affine.ATModule.Live // [error] val matrixModule: geometry.matrix.MatrixModule.Service[Any] = ???
  • 155. Display the first canvas / 2 def program(viewFrom: Pt): ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit] program(Pt(2, 2, -10)) .provide( new CanvasSerializer.PPMCanvasSerializer with RasteringModule.ChunkRasteringModule with ATModule.Live ) // Members declared in zio.blocking.Blocking // [error] val blocking: zio.blocking.Blocking.Service[Any] = ??? // [error] // [error] // Members declared in modules.RasteringModule.ChunkRasteringModule // [error] val cameraModule: modules.CameraModule.Service[Any] = ??? // [error] val worldModule: modules.WorldModule.Service[Any] = ??? // [error] // [error] // Members declared in geometry.affine.ATModule.Live // [error] val matrixModule: geometry.matrix.MatrixModule.Service[Any] = ???
  • 156. Display the first canvas / 2 def program(viewFrom: Pt): ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit] program(Pt(2, 2, -10)) .provide( new CanvasSerializer.PPMCanvasSerializer with RasteringModule.ChunkRasteringModule with ATModule.Live ) // Members declared in zio.blocking.Blocking // [error] val blocking: zio.blocking.Blocking.Service[Any] = ??? // [error] // [error] // Members declared in modules.RasteringModule.ChunkRasteringModule // [error] val cameraModule: modules.CameraModule.Service[Any] = ??? // [error] val worldModule: modules.WorldModule.Service[Any] = ??? // [error] // [error] // Members declared in geometry.affine.ATModule.Live // [error] val matrixModule: geometry.matrix.MatrixModule.Service[Any] = ???
  • 157. Display the first canvas / 3 def program(viewFrom: Pt): ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit] program(Pt(2, 2, -10)) .provide( new CanvasSerializer.PPMCanvasSerializer with RasteringModule.ChunkRasteringModule with ATModule.Live with CameraModule.Live with MatrixModule.BreezeLive with WorldModule.Live ) ) // [error] // Members declared in io.tuliplogic.raytracer.ops.model.modules.WorldModule.Live // [error] val phongReflectionModule: io.tuliplogic.raytracer.ops.model.modules.PhongReflectionModule.Service[Any] = ??? // [error] val worldHitCompsModule: io.tuliplogic.raytracer.ops.model.modules.WorldHitCompsModule.Service[Any] = ??? // [error] val worldReflectionModule: io.tuliplogic.raytracer.ops.model.modules.WorldReflectionModule.Service[Any] = ??? // [error] val worldRefractionModule: io.tuliplogic.raytracer.ops.model.modules.WorldRefractionModule.Service[Any] = ??? // [error] val worldTopologyModule: io.tuliplogic.raytracer.ops.model.modules.WorldTopologyModule.Service[Any] = ???
  • 158. Display the first canvas - /4 Group modules in trait trait BasicModules extends NormalReflectModule.Live with RayModule.Live with ATModule.Live with MatrixModule.BreezeLive with WorldModule.Live with WorldTopologyModule.Live with WorldHitCompsModule.Live with CameraModule.Live with RasteringModule.Live with Blocking.Live
  • 159. Display the first canvas - /4 Group modules in trait trait BasicModules extends NormalReflectModule.Live with RayModule.Live with ATModule.Live with MatrixModule.BreezeLive with WorldModule.Live with WorldTopologyModule.Live with WorldHitCompsModule.Live with CameraModule.Live with RasteringModule.Live with Blocking.Live
  • 160. Display the first canvas - /5 How to group typeclasses? program[F[_] : NormalReflectModule : RayModule : ATModule : MatrixModule : WorldModule : WorldTopologyModule : WorldHitCompsModule : CameraModule : RasteringModule : Blocking ] program[F[_]: BasicModules] //not so easy
  • 161. Display the first canvas - /6 Group modules def program(viewFrom: Pt): ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit] program(Pt(2, 2, -10)) .provide(new BasicModules with PhongReflectionModule.BlackWhite)
  • 162. Display the first canvas - /7 Group modules def program(viewFrom: Pt): ZIO[CanvasSerializer with RasteringModule with ATModule, RayTracerError, Unit] def run(args: List[String]): ZIO[ZEnv, Nothing, Int] = ZIO.traverse(-18 to -6)(z => program(Pt(2, 2, z)) .provide( new BasicModules with PhongReflectionModule.BlackWhite ) ).foldM(err => console.putStrLn(s"Execution failed with: $err").as(1), _ => UIO.succeed(0) )
  • 163. Live PhongReflectionModule trait Live extends PhongReflectionModule { val aTModule: ATModule.Service[Any] val normalReflectModule: NormalReflectModule.Service[Any] val lightDiffusionModule: LightDiffusionModule.Service[Any] }
  • 164. Live PhongReflectionModule program(Pt(2, 2, -10)) .provide( new BasicModules with PhongReflectionModule.Live // with PhongReflectionModule.BlackWhite )
  • 165. Live PhongReflectionModule program(Pt(2, 2, -10)) .provide( new BasicModules with PhongReflectionModule.Live // with PhongReflectionModule.BlackWhite )
  • 166. Live PhongReflectionModule program(Pt(2, 2, -10)) .provide( new BasicModules with PhongReflectionModule.Live // with PhongReflectionModule.BlackWhite )
  • 167. Live PhongReflectionModule program(Pt(2, 2, -10)) .provide( new BasicModules with PhongReflectionModule.Live // with PhongReflectionModule.BlackWhite )
  • 168. Reflect the ligtht source Describe material properties case class Material( color: Color, // the basic color ambient: Double, // ∈ [0, 1] diffuse: Double, // ∈ [0, 1] specular: Double, // ∈ [0, 1] shininess: Double, // ∈ [10, 200] )
  • 169. Live PhongReflectionModule trait Live extends PhongReflectionModule { val aTModule: ATModule.Service[Any] val normalReflectModule: NormalReflectModule.Service[Any] val lightDiffusionModule: LightDiffusionModule.Service[Any] val lightReflectionModule: LightReflectionModule.Service[Any] }
  • 170. Live PhongReflectionModule trait Live extends PhongReflectionModule { val aTModule: ATModule.Service[Any] val normalReflectModule: NormalReflectModule.Service[Any] val lightDiffusionModule: LightDiffusionModule.Service[Any] val lightReflectionModule: LightReflectionModule.Service[Any] }
  • 171. Render 3 spheres - reflect light source case class Material( pattern: Pattern, // the color pattern ambient: Double, // ∈ [0, 1] diffuse: Double, // ∈ [0, 1] specular: Double, // ∈ [0, 1] shininess: Double, // ∈ [10, 200] reflective: Double, // ∈ [0, 1] ) trait Live extends PhongReflectionModule { /* other modules */ val lightReflectionModule: LightReflectionModule.Service[Any] } trait RenderingModulesV1 extends PhongReflectionModule.Live with LightReflectionModule.Live program( from = Pt(57, 20, z), to = Pt(20, 0, 20) ).provide { new BasicModules with RenderingModulesV1 }
  • 172. Render 3 spheres - reflect light source case class Material( pattern: Pattern, // the color pattern ambient: Double, // ∈ [0, 1] diffuse: Double, // ∈ [0, 1] specular: Double, // ∈ [0, 1] shininess: Double, // ∈ [10, 200] reflective: Double, // ∈ [0, 1] ) trait Live extends PhongReflectionModule { /* other modules */ val lightReflectionModule: LightReflectionModule.Service[Any] } trait RenderingModulesV1 extends PhongReflectionModule.Live with LightReflectionModule.Live program( from = Pt(57, 20, z), to = Pt(20, 0, 20) ).provide { new BasicModules with RenderingModulesV1 }
  • 173. Render 3 spheres - reflect light source case class Material( pattern: Pattern, // the color pattern ambient: Double, // ∈ [0, 1] diffuse: Double, // ∈ [0, 1] specular: Double, // ∈ [0, 1] shininess: Double, // ∈ [10, 200] reflective: Double, // ∈ [0, 1] ) trait Live extends PhongReflectionModule { /* other modules */ val lightReflectionModule: LightReflectionModule.Service[Any] } trait RenderingModulesV1 extends PhongReflectionModule.Live with LightReflectionModule.Live program( from = Pt(57, 20, z), to = Pt(20, 0, 20) ).provide { new BasicModules with RenderingModulesV1 }
  • 174. Render 3 spheres - reflect light source case class Material( pattern: Pattern, // the color pattern ambient: Double, // ∈ [0, 1] diffuse: Double, // ∈ [0, 1] specular: Double, // ∈ [0, 1] shininess: Double, // ∈ [10, 200] reflective: Double, // ∈ [0, 1] ) trait Live extends PhongReflectionModule { /* other modules */ val lightReflectionModule: LightReflectionModule.Service[Any] } trait RenderingModulesV1 extends PhongReflectionModule.Live with LightReflectionModule.Live program( from = Pt(57, 20, z), to = Pt(20, 0, 20) ).provide { new BasicModules with RenderingModulesV1 }
  • 175. Reflective surfaces trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldHitCompsModule: WorldHitCompsModule.Service[Any] val phongReflectionModule: PhongReflectionModule.Service[Any] val worldReflectionModule: WorldReflectionModule.Service[Any]
  • 176. Reflective surfaces trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldHitCompsModule: WorldHitCompsModule.Service[Any] val phongReflectionModule: PhongReflectionModule.Service[Any] val worldReflectionModule: WorldReflectionModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] = { /* ... */ for { color <- /* standard computation of color */ reflectedColor <- worldReflectionModule.reflectedColor(world, hc) } yield color + reflectedColor } } }
  • 177. Reflective surfaces trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldHitCompsModule: WorldHitCompsModule.Service[Any] val phongReflectionModule: PhongReflectionModule.Service[Any] val worldReflectionModule: WorldReflectionModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] = { /* ... */ for { color <- /* standard computation of color */ reflectedColor <- worldReflectionModule.reflectedColor(world, hc) } yield color + reflectedColor } } }
  • 178. Reflective surfaces trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldHitCompsModule: WorldHitCompsModule.Service[Any] val phongReflectionModule: PhongReflectionModule.Service[Any] val worldReflectionModule: WorldReflectionModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] = { /* ... */ for { color <- /* standard computation of color */ reflectedColor <- worldReflectionModule.reflectedColor(world, hc) } yield color + reflectedColor } } }
  • 179. Reflective surfaces trait Live extends WorldModule { val worldTopologyModule: WorldTopologyModule.Service[Any] val worldHitCompsModule: WorldHitCompsModule.Service[Any] val phongReflectionModule: PhongReflectionModule.Service[Any] val worldReflectionModule: WorldReflectionModule.Service[Any] val worldModule: Service[Any] = new Service[Any] { def colorForRay(world: World, ray: Ray): ZIO[Any, RayTracerError, Color] = { /* ... */ for { color <- /* standard computation of color */ reflectedColor <- worldReflectionModule.reflectedColor(world, hc) } yield color + reflectedColor } } }
  • 180. Handling reflection - Live trait Live extends WorldReflectionModule { val worldModule: WorldModule.Service[Any] val worldReflectionModule = new WorldReflectionModule.Service[Any] { def reflectedColor(world: World, hitComps: HitComps, remaining: Int): ZIO[Any, RayTracerError, Color] = if (hitComps.shape.material.reflective == 0) { UIO(Color.black) } else { val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV) worldModule.colorForRay(world, reflRay, remaining).map(c => c * hitComps.shape.material.reflective ) } } }
  • 181. Handling reflection - Live trait Live extends WorldReflectionModule { val worldModule: WorldModule.Service[Any] val worldReflectionModule = new WorldReflectionModule.Service[Any] { def reflectedColor(world: World, hitComps: HitComps, remaining: Int): ZIO[Any, RayTracerError, Color] = if (hitComps.shape.material.reflective == 0) { UIO(Color.black) } else { val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV) worldModule.colorForRay(world, reflRay, remaining).map(c => c * hitComps.shape.material.reflective ) } } }
  • 182. Handling reflection - Live trait Live extends WorldReflectionModule { val worldModule: WorldModule.Service[Any] val worldReflectionModule = new WorldReflectionModule.Service[Any] { def reflectedColor(world: World, hitComps: HitComps, remaining: Int): ZIO[Any, RayTracerError, Color] = if (hitComps.shape.material.reflective == 0) { UIO(Color.black) } else { val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV) worldModule.colorForRay(world, reflRay, remaining).map(c => c * hitComps.shape.material.reflective ) } } }
  • 183. Handling reflection - Live trait Live extends WorldReflectionModule { val worldModule: WorldModule.Service[Any] val worldReflectionModule = new WorldReflectionModule.Service[Any] { def reflectedColor(world: World, hitComps: HitComps, remaining: Int): ZIO[Any, RayTracerError, Color] = if (hitComps.shape.material.reflective == 0) { UIO(Color.black) } else { val reflRay = Ray(hitComps.overPoint, hitComps.rayReflectV) worldModule.colorForRay(world, reflRay, remaining).map(c => c * hitComps.shape.material.reflective ) } } }
  • 185. Handling reflection - Noop module trait NoReflectionModule extends WorldReflectionModule { val worldReflectionModule = new WorldReflectionModule.Service[Any] { def reflectedColor( world: World, hitComps: HitComps, remaining: Int ): ZIO[Any, RayTracerError, Color] = UIO.succeed(Color.black) } }
  • 186. Handling reflection - Noop module → Red: reflective = 0.9 → Green/white: reflective = 0.6 program( from = Pt(57, 20, z), to = Pt(20, 0, 20) ).provide { new BasicModules with PhongReflectionModule.Live with WorldReflectionModule.NoReflectionModule }
  • 187. Handling reflection - Live module → Red: reflective = 0.9 → Green/white: reflective = 0.6 program( from = Pt(57, 20, z), to = Pt(20, 0, 20) ).provide { new BasicModules with PhongReflectionModule.Live with WorldReflectionModule.Live⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ }
  • 188. Alternative approach Provide partial environments val program: ZIO[RasteringModule, Nothing, Unit] val partial = program .provideSome[WorldReflectionModule]( f: WorldReflectionModule => RasteringModule ): ZIO[WorldReflectionModule, E, A] partial.provide(new WorldReflectionModule.Live)
  • 189. Conclusion - Environmental Effects ZIO[R, E, A] Build purely functional, testable, modular applications
  • 190. Conclusion - Environmental Effects ZIO[R, E, A] Build purely functional, testable, modular applications → Do not require HKT, typeclasses, etc
  • 191. Conclusion - Environmental Effects ZIO[R, E, A] Build purely functional, testable, modular applications → Do not require HKT, typeclasses, etc → Do not abuse typeclasses
  • 192. Conclusion - Environmental Effects ZIO[R, E, A] Build purely functional, testable, modular applications → Do not require HKT, typeclasses, etc → Do not abuse typeclasses → Can group capabilities
  • 193. Conclusion - Environmental Effects ZIO[R, E, A] Build purely functional, testable, modular applications → Do not require HKT, typeclasses, etc → Do not abuse typeclasses → Can group capabilities → Can provide capabilities one at a time
  • 194. Conclusion - Environmental Effects ZIO[R, E, A] Build purely functional, testable, modular applications → Do not require HKT, typeclasses, etc → Do not abuse typeclasses → Can group capabilities → Can provide capabilities one at a time → Are not dependent on implicits (survive refactoring)
  • 196. Conclusion - Environmental Effects → Low entry barrier, very mechanical
  • 197. Conclusion - Environmental Effects → Low entry barrier, very mechanical → Macros help with boilerplate
  • 198. Conclusion - Environmental Effects → Low entry barrier, very mechanical → Macros help with boilerplate → Handle circular dependencies
  • 199. Conclusion - Environmental Effects → Low entry barrier, very mechanical → Macros help with boilerplate → Handle circular dependencies → Try it out! $
  • 200. Conclusion - Environmental Effects → Low entry barrier, very mechanical → Macros help with boilerplate → Handle circular dependencies → Try it out! $ → Join ZIO Discord channel