SlideShare uma empresa Scribd logo
1 de 105
Baixar para ler offline
1
, DOTTY, AND THE
AWESOME POWER OF
'inline'
Alexander Ioffe

@deusaquilus

https://github.com/deusaquilus/dotty_test/tree/fs_2020_talk/
2
Title One
What is Quill?
val q = quote {
query[Person].filter(p => p.name == "Joe")
}
sbt:my-project> compile
[info] Compiling 1 Scala source to /Users...
SELECT p.name, p.age FROM Person p WHERE p.name = 'Joe'
case class Person(name: String, age: Int)
3
Title One
What is Quill...
val q = quote {
select[Person].filter(p => p.name == "Joe")
}
sbt:my-project> compile
[info] Compiling 1 Scala source to /Users...
SELECT p.name, p.age FROM Person p WHERE p.name = 'Joe'
case class Person(name: String, age: Int)
4
Scala Compiler
Query
Compiler
Query[T]AST
SELECT …
FROM
PERSON
Parsing
Q
uery
String
Your Application
Query
CompilerAST
SELECT …
FROM
PERSON
Parsing
Q
uery
String
JDBC
Quoted
Query[T]
Quoted
T
Result
Your
Application
JDBCT
Result
Total Runtime
5
Then Your DBA Asks…
In a fit of Desperation at 3:00 AM
6
Bring on the Complexity…
sbt:my-project> compile
[info] Compiling 1 Scala source to /Users...
SELECT o1.id, o1.name, o1.population, c1.id, c1.name, c1.countryId,
p1.id, p1.name, p1.age, p1.companyId, af.host, af.of, p2.id, p2.name,
p2.age, p2.companyId FROM Country o1
INNER JOIN Company c1 ON c1.countryId = o1.id
INNER JOIN Person p1 ON p1.companyId = c1.id
LEFT JOIN ClientAffiliation af ON af.of = p1.id
LEFT JOIN Person p2 ON af.host = p2.id
val q = quote {
for {
o1 <- query[Country]
c1 <- query[Company].join(c1 => c1.countryId == o1.id)
p1 <- query[Person].join(p1 => p1.companyId == c1.id)
af <- query[ClientAffiliation].leftJoin(af => af.of == p1.id)
p2 <- query[Person].leftJoin(p2 => af.map(_.host).exists(v => v == p2.id))
} yield (o1, c1, p2, af, p2)
}
run(q)
7
Limitation - Must Lift Runtime Values
val q = quote {
query[Person].filter(p => p.name == lift(runtimeVar))
}
sbt:my-project> compile
[info] Compiling 1 Scala source to /Users...
SELECT p.name, p.age FROM Person p WHERE p.name = ?
case class Person(name: String, age: Int)
8
Title One
Limitation - Runtime Switches
val q =
if (someRuntimeCondition)
quote { query[Person].filter(p => p.name == "Joe") }
else
quote { query[Person] }
sbt:my-project> compile
[info] Compiling 1 Scala source to /Users...
Dynamic Query
case class Person(name: String, age: Int)
9
Limitation - Dynamic Filters
sbt:my-project> compile
[info] Compiling 1 Scala source to /Users...
Dynamic Query
case class Person(name: String, age: Int)
val filters = Map("name" -> "Joe", "age" -> "22")
val q = quote {
query[Person].filter(p =>
col(p, "name") == lift(filters("name")) AND
col(p, "age") == lift(filters("age"))
)
}
10
Title One
Limitation - Encapsulating Methods
val q = quote {
joes(query[Person])
}
sbt:my-project> compile
[info] Compiling 1 Scala source to /Users...
Dynamic Query
case class Person(name: String, age: Int)
def joes(q: Query[People]) =
q.filter(p => p.name == "Joe")
11
Title One
This…
val q: Quoted[Query[Person]] =
quote { query[Person].filter(p => p.name == "Joe") }
sbt:my-project> compile
[info] Compiling 1 Scala source to /Users...
Dynamic Query
case class Person(name: String, age: Int)
12
Title One
… Is Really This
val q: AnyRef with Quoted[Query[Person]] { def quoted… def lifted… } =
quote { query[Person].filter(p => p.name == "Joe") }
sbt:my-project> compile
[info] Compiling 1 Scala source to /Users...
Dynamic Query
case class Person(name: String, age: Int)
13
Title One
sbt:my-project> compile
[info] Compiling 1 Scala source to /Users...
Dynamic Query
I CAN (EASILY) GET COMPILE-TIME QUERIES
… BUT GIVE UP MOST LANGUAGE FEATURES
14
15
Before We Get Started...
16
Title One
I was told there would be Macros
object Mac {
inline def enter(str: String): Unit = ${ enterImpl('str) }
def enterImpl(str: Expr[String])(using qctx: QuoteContext): Expr[Unit] = {
import qctx.tasty._
'{ () }
}
}
NOTE: This Slide was skipped
in the talk to save time.
17
Title One
object Mac {
inline def enter(str: String): Unit = ${ enterImpl('str) }
def enterImpl(str: Expr[String])(using qctx: QuoteContext): Expr[Unit] = {
import qctx.tasty._
println(pprint(str.unseal))
'{ () }
}
}
I was told there would be Macros
NOTE: This Slide was skipped
in the talk to save time.
18
Title One
object Mac {
inline def enter(str: String): Unit = ${ enterImpl('str) }
def enterImpl(str: Expr[String])(using qctx: QuoteContext): Expr[Unit] = {
import qctx.tasty._
println(pprint(str.unseal))
'{ () }
}
}
I was told there would be Macros
NOTE: This Slide was skipped
in the talk to save time.
19
Title One
object Mac {
inline def enter(str: String): Unit = ${ enterImpl('str) }
def enterImpl(str: Expr[String])(using qctx: QuoteContext): Expr[Unit] = {
import qctx.tasty._
println(pprint(str.unseal))
'{ () }
}
}
val greeting = "Hello"
Mac.enter(greeting)
sbt:dotty-simple> compile
[info] Compiling 1 Scala source to /Users...
Inlined(Thicket(List()), List(), Ident(greeting))
I was told there would be Macros
NOTE: This Slide was skipped
in the talk to save time.
20
Title One
Inline All The Things!
object Mac {
inline def enter(inline str: String): Unit = ${ enterImpl('str) }
def enterImpl(str: Expr[String])(using qctx: QuoteContext): Expr[Unit] = {
import qctx.tasty._
println(pprint(str.unseal))
'{ () }
}
}
inline def greeting = "Hello"
Mac.enter(greeting)
NOTE: This Slide was skipped
in the talk to save time.
Title One
inline def greeting = "Hello"
PrintMac(greeting)
[info] Compiling 1 Scala source to /Users...
Inlined(
Thicket(List()),
List(),
Inlined(
Ident(greeting),
List(),
Literal(("Hello")),
)
)
Title One
val greeting = "Hello"
PrintMac(greeting)
[info] Compiling 1 Scala source to /Users...
Inlined(
Thicket(List()),
List(),
Ident(greeting)
)
Inlined(
Thicket(List()), List(),
Inlined(
Ident(combo), List(),
Typed(
Apply(
Select(
Inlined(Ident(greeting), List(), Typed(Literal(("Hello")),TypeStuff())),
+
),
List(Inlined(Ident(suffix),List(),Typed(Literal((" world")),TypeStuff())))
),
TypeStuff()
)
)
)
inline def greeting = "Hello"
inline def suffix = " world"
inline def combo = greeting + suffix
PrintMac(combo)
Inlined(<originalArg>, <binds>, <expansion>)
Inlined(
Thicket(List()),List(),
Inlined(
Ident(combo),List(),
Typed(
Apply(
Select(
Apply(
Select(
Inlined(Ident(greeting),List(),Typed(Literal(("Hello")),TypeStuff())),
+
),
List(Inlined(Ident(suffix),List(),Typed(Literal((" world")),TypeStuff())))
),
+
),
List(Inlined(Ident(suffix2),List(),Typed(Literal((" today!")),TypeStuff())))
),
TypeStuff()
)
)
)
inline def greeting = "Hello"
inline def suffix = " world"
inline def suffix2 = " today!"
inline def combo = greeting + suffix + suffix2
Mac.enter(combo)
Inlined(<originalArg>, <binds>, <expansion>)
NOTE: This Slide was skipped
in the talk to save time.
def enterImpl(str: Expr[String])(using qctx: QuoteContext): Expr[Unit] = {
import qctx.tasty._
println(pprint(str.unseal.underlyingArgument))
'{ () }
}
def enterImpl(str: Expr[String])(using qctx: QuoteContext): Expr[Unit] = {
import qctx.tasty._
println(pprint(str.unseal))
'{ () }
}
Typed(
Literal(("Hello")),
TypeStuff()
)
Typed(
Apply(
Select(
Apply(
Select(
Typed(Literal(("Hello")),TypeStuff()),
+
),
List(Typed(Literal((" world")),TypeStuff()))
),
+
),
List(Typed(Literal((" today!")),TypeStuff()))
),
TypeStuff()
)
Inlined(<originalArg>, <binds>, <expansion>)
NOTE: This Slide was skipped
in the talk to save time.
25
Phase Consistency Principle
A.K.A Passing t he Buck… in a go o d way!
Splice it In Throw it Out
return '{ func($str) } val bar: Expr[Bar] =
'{ change($foo) }
∀ pieceOfCode: Expr[T]
val str: Expr[T] = …
'{ () }
Macros Will Always Be Hygienic∴
Transform It
Then do one of the former…
NOTE: This Slide was skipped
in the talk to save time.
26
class Space {
class InnerSpace {
inline def hello = Mac.passThrough("hello")
}
inline def world = Mac.passThrough(new InnerSpace().hello)
}
inline def today = Mac.passThrough(new Space().world + " today")
println(today)
inline def passThrough(inline str: String): String = ${ passThroughImpl('str) }
def passThroughImpl(str: Expr[String])(using qctx: QuoteContext): Expr[String] = {
import qctx.tasty._
println(pprint(str.unseal))
str
}
Phase Consistency Principle
A.K.A Passing t he Buck… in a go o d way!
This is not available
By the time this happens
27
class Space {
class InnerSpace {
inline def hello = Mac.passThrough("hello")
}
inline def world = Mac.passThrough(new InnerSpace().hello + " world”)
}
inline def today = Mac.passThrough(new Space().world + " today")
println(today)
Phase Consistency Principle
A.K.A Passing t he Buck… in a go o d way!
Inlined(Thicket(List()),List(),Apply(Select(
Inlined(Select(Apply(Select(New(Ident(Space)), <init>), List()), world),List(),
Inlined(Apply(Select(Ident(Mac), passThrough),List(Apply(Select(
Inlined(Select(Apply(Select(New(Ident(InnerSpace)), <init>), List()), hello),List(),
Inlined(Apply(Select(Ident(Mac), passThrough), List(Literal(("hello")))),List(),
Inlined(Thicket(List()), List(), Literal(("hello")))),
)
), + ), List(Literal(("world")))))
),
List(),
Inlined(Thicket(List()),List(),Apply(Select(
Inlined(Select(Apply(Select(New(Ident(InnerSpace)), <init>), List()), hello),List(),
Inlined(Apply(Select(Ident(Mac), passThrough), List(Literal(("hello")))),List(),
Inlined(Thicket(List()), List(), Literal(("hello")))),
), + ), List(Literal(("world"))))),
),
)), + ), List(Literal((" today")))
)
) Inlined(<originalArg>, <binds>, <expansion>)
28
class Space {
class InnerSpace {
inline def hello = Mac.passThrough("hello")
}
inline def world = Mac.passThrough(new InnerSpace().hello + " world”)
}
inline def today = Mac.passThrough(new Space().world + " today")
println(today)
Phase Consistency Principle
A.K.A Passing t he Buck… in a go o d way!
Inlined(Thicket(List()),List(),Apply(Select(
Inlined(Select(Apply(Select(New(Ident(Space)), <init>), List()), world),List(),
Inlined(Apply(Select(Ident(Mac), passThrough),List(Apply(Select(
Inlined(Select(Apply(Select(New(Ident(InnerSpace)), <init>), List()), hello),List(),
Inlined(Apply(Select(Ident(Mac), passThrough), List(Literal(("hello")))),List(),
Inlined(Thicket(List()), List(), Literal(("hello")))),
)
), + ), List(Literal(("world")))))
),
List(),
Inlined(Thicket(List()),List(),Apply(Select(
Inlined(Select(Apply(Select(New(Ident(InnerSpace)), <init>), List()), hello),List(),
Inlined(Apply(Select(Ident(Mac), passThrough), List(Literal(("hello")))),List(),
Inlined(Thicket(List()), List(), Literal(("hello")))),
), + ), List(Literal(("world"))))),
),
)), + ), List(Literal((" today")))
)
) Inlined(<originalArg>, <binds>, <expansion>)
Applying In
29
class Space {
class InnerSpace {
inline def hello = Mac.passThrough("hello")
}
inline def world = Mac.passThrough(new InnerSpace().hello + " world”)
}
inline def today = Mac.passThrough(new Space().world + " today")
println(today)
Phase Consistency Principle
A.K.A Passing t he Buck… in a go o d way!
Inlined(Thicket(List()),List(),Apply(Select(
Inlined(Select(Apply(Select(New(Ident(Space)), <init>), List()), world),List(),
Inlined(Apply(Select(Ident(Mac), passThrough),List(Apply(Select(
Inlined(Select(Apply(Select(New(Ident(InnerSpace)), <init>), List()), hello),List(),
Inlined(Apply(Select(Ident(Mac), passThrough), List(Literal(("hello")))),List(),
Inlined(Thicket(List()), List(), Literal(("hello")))),
)
), + ), List(Literal(("world")))))
),
List(),
Inlined(Thicket(List()),List(),Apply(Select(
Inlined(Select(Apply(Select(New(Ident(InnerSpace)), <init>), List()), hello),List(),
Inlined(Apply(Select(Ident(Mac), passThrough), List(Literal(("hello")))),List(),
Inlined(Thicket(List()), List(), Literal(("hello")))),
), + ), List(Literal(("world"))))),
),
)), + ), List(Literal((" today")))
)
) Inlined(<originalArg>, <binds>, <expansion>)
Applying In
Expanding Out
30
class Space {
class InnerSpace {
inline def hello = Mac.passThrough("hello")
}
inline def world = Mac.passThrough(new InnerSpace().hello + " world”)
}
inline def today = Mac.passThrough(new Space().world + " today")
println(today)
Phase Consistency Principle
A.K.A Passing t he Buck… in a go o d way!
Inlined(<originalArg>, <binds>, <expansion>)
Applying In
Expanding Out
Built-in Lineage
31
class Space {
class InnerSpace {
inline def hello = Mac.passThrough("hello")
}
inline def world = Mac.passThrough(new InnerSpace().hello + " world”)
}
inline def today = Mac.passThrough(new Space().world + " today")
println(today)
Phase Consistency Principle
A.K.A Passing t he Buck… in a go o d way!
Built-in LineageHygienic ⊃
32
In Scala 3 Quill Quotes will be Inline
… an d life will b e aweso me !
inline def q = quote {
query[Person]
}
⊃inline def p =
query[Person]
33
inline def q = quote {
query[Person]
}
inline def p =
query[Person] ⊃
Inlined(Quoted(EntityQuery[Person] …)
Inlined(EntityQuery[Person])
34
inline def q = quote {
p
}
inline def p =
query[Person]
Inlined(p …)
Inlined(EntityQuery[Person])inline def p =
35
inline def q = quote {
p
}
inline def p =
query[Person]
Inlined(Inlined(EntityQuery[Person]) …)
Inlined(EntityQuery[Person])inline def p =
The CompilerDoes This!
36
Inlined(Inlined(EntityQuery[Person]) …)
Inlined(EntityQuery[Person])inline def p =
inline def q = quote {
p
}
inline def p =
query[Person]
KEEP CALM
THROW
WHITEBOX
MACROS
OUT OF
A WINDOW
37
Inlined(Inlined(EntityQuery[Person]) …)
Inlined(EntityQuery[Person])inline def p =
inline def q = quote {
p
}
inline def p =
query[Person]
All other Language-Constructs must now
be compatible with you!
All other Language - Constructs
must now be compatible with you!
38
inline def q = quote {
p
}
inline def p =
query[Person]
In Scala 3 Quill Quotes will be Inline
… an d life will b e aweso me !
sbt:dotty-simple> compile
[info] Compiling 1 Scala source to /Users...
SELECT x.name, x.age FROM Person x
39
In Scala 3 Quill Quotes will be Inline
… an d life will b e aweso me !
inline def onlyJoes =
(p: Person) => p.name == "Joe"
inline def q = quote {
query[Person].filter(onlyJoes)
}
List(Person("Joe", 22), Person("Jack", 33)).filter(onlyJoes)
[info] running miniquill.MiniExample3_InlineFilter
List(Person(Joe,22))
MiniExample3_InlineFilter.scala
40
inline def q = quote {
joes(query[Person])
}
inline def joes(inline q: Query[Person]) =
q.filter(p => p.name == "Joe")
sbt:dotty-simple> compile
[info] Compiling 1 Scala source to /Users...
SELECT x.name, x.age FROM Person x WHERE p.name = 'Joe'
41
inline def joes(inline q: Query[Person], inline filter: Boolean) =
inline if (filter)
q.filter(p => p.name == "Joe")
else
q
inline def q = quote {
joes(query[Person], true)
}
sbt:dotty-simple> compile
[info] Compiling 1 Scala source to /Users...
SELECT x.name, x.age FROM Person x WHERE p.name = 'Joe'
MiniExample1_Joes.scala
42
inline def joes(inline q: Query[Person], inline filter: Boolean) =
inline if (filter)
q.filter(p => p.name == "Joe")
else
q
inline def q = quote {
joes(query[Person], false)
}
sbt:dotty-simple> compile
[info] Compiling 1 Scala source to /Users...
SELECT x.name, x.age FROM Person x
MiniExample1_Joes.scala
43
val service = HttpRoutes.of[IO] {
case GET -> Root / "people" :? JoesMatcher(isJoe: Boolean) =>
Ok(stream(joinTables(lift(role), {
val q =
if (isJoe)
quote { query[Person].filter(p => p.name == "Joe") }
else
quote { query[Person] }
run(q)
})).transact(xa).map(_.asJson))
val service = HttpRoutes.of[IO] {
case GET -> Root / "people" :? JoesMatcher(isJoe: Boolean) =>
Ok(stream(joinTables(lift(role), {
val q1 = quote { query[Person].filter(p => p.name == "Joe") }
val q2 = quote { query[Person] }
if (isJoe) run(q1) else run(q2)
})).transact(xa).map(_.asJson))
[info] Compiling 1 Scala source to /Users
Dynamic Query
[info] Compiling 1 Scala source to /Users
SELECT p.name, p.age FROM Person p
WHERE p.name = 'Joe'
SELECT p.name, p.age FROM Person p
Filters with Http4s
get: /app/people?isJoe=true
44
val service = HttpRoutes.of[IO] {
case GET -> Root / "people" :? JoesMatcher(isJoe: Boolean) =>
Ok(stream(joinTables(lift(role), {
inline def people(inline isJoeInline: Boolean) =
inline if (isJoeInline)
query[Person].filter(p => p.name == "Joe")
else
query[Person]
if (isJoe) run(q, true) else run(q, false)
})).transact(xa).map(_.asJson))
[info] Compiling 1 Scala source to /Users
SELECT p.name, p.age FROM Person p
WHERE p.name = 'Joe'
SELECT p.name, p.age FROM Person p
Filters with Http4s
get: /app/people?isJoe=true
45
inline def liftOrAny(inline field: String, inline filter: Option[String]) =
lift(filter.getOrElse(null)) == field ||
lift(filter.getOrElse(null)) == null
val runtimeValue = Some("Joe") // or None
inline def q = quote {
query[Person].filter(p => liftOrAny(p.name, runtimeValue))
}
SELECT p.name, p.age FROM Person p WHERE ? = p.name OR ? IS NULL
MiniExample2_LiftOrAny.scala
Optional Filters
46
inline def liftOrAny(inline field: String, inline filter: Option[String]) =
lift(filter.getOrElse(null)) == field ||
lift(filter.getOrElse(null)) == null
val runtimeValue = Some("Joe")
inline def q = quote {
query[Person].filter(p => liftOrAny(p.name, runtimeValue))
}
SELECT p.name, p.age FROM Person p WHERE ? = p.name OR ? IS NULL
at runtime…
filter
Some("Joe")
None
WHERE 'Joe' = p.name OR 'Joe' IS NULL
WHERE null = p.name OR null IS NULL
Optional Filters
47
inline def liftOrAny(inline field: String, inline filter: Option[String]) =
lift(filter.getOrElse(null)) == field ||
lift(filter.getOrElse(null)) == null
SELECT p.name, p.age FROM Person p WHERE ? = p.name OR ? IS NULL
at runtime…
filter
Some("Joe")
None
WHERE 'Joe' = p.name OR 'Joe' IS NULL
WHERE null = p.name OR null IS NULL
val service = HttpRoutes.of[IO] {
case GET -> Root / "people" / NameMatcher(nameOpt: Option[String]) =>
Ok(stream(joinTables(lift(role), {
run { query[Person].filter(p => liftOrAny(p.name, nameOpt) }
})).transact(xa).map(_.asJson))
get: /app/people or /app/people?name=Joe
48
SELECT p.firstName, p.lastName, p.age
FROM Person p
WHERE
(p.firstName = ? OR ? IS NULL) AND
(p.lastName = ? OR ? IS NULL) AND
(p.age = ? OR ? IS NULL) AND
true
extension [T](inline q: EntityQuery[T]):
inline def filterByKeys(inline map: Map[String, String]): T =
q.filter(p => MapProc[T, PrepareRow](p, map, null, (a, b) => (a == b) || (b == (null) ) ))
case class Person(firstName: String, lastName: String, age: Int)
val values: Map[String, String] = Map("firstName" -> "Joe", "age" -> "22")
inline def q = quote {
query[Person].filterByKeys(values)
}
MiniExample_LiftByKeys.scala
49
SELECT p.firstName, p.lastName, p.age
FROM Person p
WHERE
(p.firstName = values.getOrElse("firstName",null) OR values.getOrElse("firstName",null) IS NULL) AND
(p.lastName = values.getOrElse("lastName",null) OR values.getOrElse("lastName",null) IS NULL) AND
(p.age = values.getOrElse("age",null) OR values.getOrElse("age",null) IS NULL) AND
true
case class Person(firstName: String, lastName: String, age: Int)
val values: Map[String, String] = Map("firstName" -> "Joe", "age" -> "22")
inline def q = quote {
query[Person].filterByKeys(values)
}
MiniExample_LiftByKeys.scala
extension [T](inline q: EntityQuery[T]):
inline def filterByKeys(inline map: Map[String, String]): T =
q.filter(p => MapProc[T, PrepareRow](p, map, null, (a, b) => (a == b) || (b == (null) ) ))
50
SELECT p.firstName, p.lastName, p.age
FROM Person p
WHERE
(p.firstName = 'Joe' OR 'Joe' IS NULL) AND
(p.lastName = null OR null IS NULL) AND
(p.age = '22' OR '22' IS NULL) AND
true
case class Person(firstName: String, lastName: String, age: Int)
val values: Map[String, String] = Map("firstName" -> "Joe", "age" -> "22")
inline def q = quote {
query[Person].filterByKeys(values)
}
MiniExample_LiftByKeys.scala
extension [T](inline q: EntityQuery[T]):
inline def filterByKeys(inline map: Map[String, String]): T =
q.filter(p => MapProc[T, PrepareRow](p, map, null, (a, b) => (a == b) || (b == (null) ) ))
51
SELECT p.firstName, p.lastName, p.age
FROM Person p
WHERE
(p.firstName = 'Joe' OR 'Joe' IS NULL) AND
(p.lastName = null OR null IS NULL) AND
(p.age = '22' OR '22' IS NULL) AND
true
get: /app/people?firstName=Joe&age=22
val service = HttpRoutes.of[IO] {
case GET -> Root / "people" :? (multiFilters: Map[String, Seq[String]]) =>
Ok(stream(joinTables(lift(role), {
val filters: Map[String, String] = multiFilters.flatMap((k, v) => (k, v.take(1))
val q = quote {
query[Person].filterByKeys(filters)
}
run(q)
})).transact(xa).map(_.asJson))
52
inline def oneOf(inline list: List[String], inline column:String): Boolean = {
inline if (ListProc.isNil(list))
false
else
ListProc.index(list, 0) == column || oneOf(ListProc.tail(list), column)
}
Processing with Recursive Inlines
First Curse, then Recurse!
inline def q = quote {
query[Person].filter(p => oneOf(List("Joe", "Jack"), p.name))
}
[info] Compiling 1 Scala source to /Users…
SELECT p.name, p.age FROM Person p WHERE 'Joe' = p.name OR 'Jack' = p.name OR false
case class Person(name: String, age: Int)
MiniExample_OneOf.scala
NOTE: This Slide was skipped
in the talk to save time.
53
inline def oneOf(inline list: List[String], inline column:String): Boolean = {
inline if (ListProc.isNil(list))
false
else
ListProc.index(list, 0) == column || oneOf(ListProc.tail(list), column)
}
Processing with Recursive Inlines
First Curse, then Recurse!
inline def q = quote {
query[Person].filter(n => oneOf(List(n.lastStatus, n.backupStatus), p.name))
}
SELECT n.name, n.age FROM Node n
WHERE n.lastStatus = n.status OR n.backupStatus = n.status OR false
case class Node(status: String, lastStatus: String, backupStatus: Int)
MiniExample_OneOf.scala
NOTE: This Slide was skipped
in the talk to save time.
54
inline def oneOf(inline list: List[String], inline column:String): Boolean = {
inline if (ListProc.isNil(list))
false
else
ListProc.index(list, 0) == column || oneOf(ListProc.tail(list), column)
}
Processing with Recursive Inlines
First Curse, then Recurse!
inline def q = quote {
query[Person].filter(n => oneOf(List(n.lastStatus,"restarting"), n.status))
}
SELECT n.name, n.age FROM Node n
WHERE n.lastStatus = n.status OR 'restarting' = n.status OR false
case class Node(status: String, lastStatus: String, backupStatus: Int)
MiniExample_OneOf.scala
NOTE: This Slide was skipped
in the talk to save time.
55
Inline Type-Classes
THERE’S ACTUALLY A GOOD REASON TO DO THIS!
NOTE: This Slide was skipped
in the talk to save time.
trait Functor[F[_]]:
inline def map[A, B](inline xs: F[A], inline f: A => B): F[B]
class ListFunctor extends Functor[List]:
inline def map[A, B](inline xs: List[A], inline f: A => B): List[B] = xs.map(f)
class QueryFunctor extends Functor[Query]:
inline def map[A, B](inline xs: Query[A], inline f: A => B): Query[B] = xs.map(f)
inline given listFunctor as ListFunctor = new ListFunctor
inline given queryFunctor as QueryFunctor = new QueryFunctor
inline def doMap[F[_], A, B](inline from: F[A], inline f: A => B)(using inline fun: Functor[F]): F[B] =
fun.map(from, f)
val list2 = doMap(List(1,2,3), (i: Int) => i + 1) // Runtime
inline def q = quote { doMap(select[Person], (p: Person) => p.name) } // Compile-Time!
An Inline Functor…
N o , r e a l l y. T h i s i s a c t u a l l y p o s s i b l e !
[info] Compiling 1 Scala source to /Users...
Compile Time Query Is: SELECT p.name FROM Person p
runMain miniquill.InlineMacroTest1FunctionalTypeclass
List(2, 3, 4) TypeclassExample_FunctorOldStyle.scala
NOTE: This Slide was skipped
in the talk to save time.
An Inline Functor… Dotty Idiomatically!
Tell t hem you saw it h ere first!
trait Functor[F[_]]:
extension [A, B](inline x: F[A]):
inline def map(inline f: A => B): F[B]
inline given Functor[List]:
extension [A, B](inline xs: List[A])
inline def map(inline f: A => B): List[B] = xs.map(f)
inline given Functor[Query]:
extension [A, B](inline xs: Query[A])
inline def map(inline f: A => B): Query[B] = xs.map(f)
extension [F[_], A, B](inline from: F[A])(using inline fun: Functor[F]):
inline def mapF(inline f: A => B) = from.map(f)
println( List(1,2,3).mapF(i => i + 1) )
inline def q = quote { select[Person].up.mapF(p => p.name) }
TypeclassExample_Functor.scala
NOTE: This Slide was skipped
in the talk to save time.
… and a Monad!
We l l , n o t r e a l l y a M o n a d s i n c e w e d o n ’ t h a v e P u r e .
Actually it ’s th e Flat Map Type cla ss
trait Monad[F[_]] extends Functor[F]:
extension [A, B](inline x: F[A]):
inline def map(inline f: A => B): F[B]
inline def flatMap(inline f: A => F[B]): F[B]
inline given Monad[List]:
extension [A, B](inline xs: List[A])
inline def map(inline f: A => B): List[B] = xs.map(f)
inline def flatMap(inline f: A => List[B]): List[B] = xs.flatMap(f)
inline given Monad[Query]:
extension [A, B](inline xs: Query[A])
inline def map(inline f: A => B): Query[B] = xs.map(f)
inline def flatMap(inline f: A => Query[B]): Query[B] = xs.flatMap(f)
extension [F[_], A, B](inline from: F[A])(using inline fun: Monad[F]):
inline def mapM(inline f: A => B) = from.map(f)
inline def flatMapM(inline f: A => F[B]) = from.flatMap(f)
inline def q = quote { select[Person].mapF(p => p.name) }
inline def q = quote { select[Person].flatMapM(p => query[Address]) }
TypeclassExample_Monad.scala
NOTE: This Slide was skipped
in the talk to save time.
trait For[F[_]]:
extension [A, B](inline x: F[A]):
inline def map(inline f: A => B): F[B]
inline def flatMap(inline f: A => F[B]): F[B]
inline def withFilter(inline f: A => Boolean): F[A]
inline given For[List]:
extension [A, B](inline xs: List[A])
inline def map(inline f: A => B): List[B] = xs.map(f)
inline def flatMap(inline f: A => List[B]): List[B] = xs.flatMap(f)
inline def withFilter(inline f: A => Boolean): List[A] = xs.filter(f)
inline given For[Query]:
extension [A, B](inline xs: Query[A])
inline def map(inline f: A => B): Query[B] = xs.map(f)
inline def flatMap(inline f: A => Query[B]): Query[B] = xs.flatMap(f)
inline def withFilter(inline f: A => Boolean): Query[A] = xs.withFilter(f)
Comprehend This!
… a nd you will be save d!
object UseCase:
extension [F[_]](inline people: F[Person])(using inline fun: For[F]):
inline def joesAddresses(inline addresses: F[Address]) =
for {
p <- people if (p.name == "Joe")
a <- addresses if (p.id == a.fk)
} yield (p, a)
TypeclassExample_For.scala
NOTE: This Slide was skipped
in the talk to save time.
Comprehend This!
… a nd you will be save d!
object UseCase:
extension [F[_]](inline people: F[Person])(using inline fun: For[F]):
inline def joesAddresses(inline addresses: F[Address]) =
for {
p <- people if (p.name == "Joe")
a <- addresses if (p.id == a.fk)
} yield (p, a)
[info] Compiling 1 Scala source to /Users…
SELECT p.id, p.name, p.age, a.fk, a.street, a.zip FROM Person p, Address a
WHERE p.name = 'Joe' AND p.id = a.fk
val peopleL = List(Person(1, "Joe", 22), Person(2, "Jack", 33), Person(3, "James", 44))
val addressesL = List(Address(1, "123 St.", 111), Address(2, "456 St.", 222), Address(3, "789 St.", 333))
[info] running miniquill.InlineMacroTest1FunctionalTypeclassIdiomatic
List((Person(1,Joe,22),Address(1,123 St.,111)))
inline def q = quote { people.joesAddresses(addresses) }
println(peopleL.joesAddresses(addressesL))
Compile Time
Runtime
TypeclassExample_For.scala
NOTE: This Slide was skipped
in the talk to save time.
Log
Time1 - Node1 Update
Time2 - Node2 Update
Time3 - Node1 Update
Time4 - Node1 Update
Time5 - Node2 Update
Log
Time1 - Master1 Update
Time2 - Master2 Update
Time3 - Master3 Update
Time4 - Master1 Update
Time5 - Master3 Update
Log
Time1 - Node1 Update
Time2 - Node3 Update
Time3 - Node3 Update
Time4 - Node2 Update
Time5 - Node2 Update
A Practical Example
… you say bo ring, I say be lie vab le !
N o d e Master Wo r k e r
Latest Node Status
id -mestamp status
1 4 UP
2 5 DOWN
Latest Master Status
id lastCheck state
1 5 WAITING
2 2 WAITING
3 5 FINISHED
Latest Worker Status
shard lastTime reply
1 5 BUSY
2 2 BUSY
3 5 DONE
Node
id -mestamp status
1 1 UP
2 2 UP
1 3 DOWN
1 4 UP
2 5 DOWN
Master
id lastCheck state
1 1 IDLE
2 2 WAITING
3 3 WAITING
1 4 WAITING
3 5 FINISHED
Worker
shard lastTime reply
1 1 BUSY
3 2 BUSY
3 3 DONE
2 4 DONE
2 5 BUSY
A Practical Example
… you say bo ring, I say be lie vab le !
Latest Node Status
id -mestamp status
1 4 UP
2 5 DOWN
Latest Master Status
id lastCheck state
1 5 WAITING
2 2 WAITING
3 5 FINISHED
Latest Worker Status
shard lastTime reply
1 5 BUSY
2 2 BUSY
3 5 DONE
SELECT 

a.id, a.timestamp, a.status

FROM Node a 

LEFT JOIN Node b 

ON b.id = a.id 

AND b.timestamp > a.timestamp 

WHERE b.id IS NULL
SELECT 

a.key, a.lastCheck, a.state

FROM Master a 

LEFT JOIN Master b 

ON b.key = a.key 

AND b.lastCheck > a.lastCheck 

WHERE b.key IS NULL
SELECT 

a.shard, a.lastTime, a.reply 

FROM Worker a 

LEFT JOIN Worker b 

ON b.shard = a.shard 

AND b.lastTime > a.lastTime 

WHERE b.shard IS NULL
A Practical Example
… you say bo ring, I say be lie vab le !
TypeclassUsecase_Repetitive.scala
Latest Node Status
id -mestamp status
1 4 UP
2 5 DOWN
Latest Master Status
id lastCheck state
1 5 WAITING
2 2 WAITING
3 5 FINISHED
Latest Worker Status
shard lastTime reply
1 5 BUSY
2 2 BUSY
3 5 DONE
inline def masters = quote {
query[Master].leftJoin(query[Master])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
inline def nodes = quote {
query[Node].leftJoin(query[Node])
.on((a, b) =>
b.id == a.id &&
b.timestamp > a.timestamp
)
.filter((a, b) =>
b.map(_.id).isEmpty)
.map((a, b) => a)
}
inline def workers = quote {
query[Worker].leftJoin(query[Worker])
.on((a, b) =>
b.shard == a.shard &&
b.lastTime > a.lastTime
)
.filter((a, b) =>
b.map(_.shard).isEmpty)
.map((a, b) => a)
}
A Practical Example
… you say bo ring, I say be lie vab le !
Latest Node Status
id -mestamp status
1 4 UP
2 5 DOWN
Latest Master Status
id lastCheck state
1 5 WAITING
2 2 WAITING
3 5 FINISHED
Latest Worker Status
shard lastTime reply
1 5 BUSY
2 2 BUSY
3 5 DONE
inline def masters = quote {
query[Master].leftJoin(query[Master])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
inline def nodes = quote {
query[Node].leftJoin(query[Node])
.on((a, b) =>
b.id == a.id &&
b.timestamp > a.timestamp
)
.filter((a, b) =>
b.map(_.id).isEmpty)
.map((a, b) => a)
}
inline def workers = quote {
query[Service].leftJoin(query[Service])
.on((a, b) =>
b.shard == a.shard &&
b.lastTime > a.lastTime
)
.filter((a, b) =>
b.map(_.shard).isEmpty)
.map((a, b) => a)
}
A Practical Example
… you say bo ring, I say be lie vab le !
inline def masters = quote {
query[Master].leftJoin(query[Master])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
inline def masters = quote {
query[Entity].leftJoin(query[Entity])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
inline def masters = quote {
query[Switch].leftJoin(query[Switch])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
Latest Node En-ty
id -mestamp status
1 4 UP
2 5 DOWN
Latest Master Service
id lastCheck state
1 5 WAITING
2 2 WAITING
3 5 FINISHED
Latest Worker Switch
shard lastTime reply
1 5 BUSY
2 2 BUSY
3 5 DONE
Latest Node App
id -mestamp status
1 4 UP
2 5 DOWN
Latest Master Bridge
id lastCheck state
1 5 WAITING
2 2 WAITING
3 5 FINISHED
Latest Worker Combo
shard lastTime reply
1 5 BUSY
2 2 BUSY
3 5 DONE
inline def masters = quote {
query[Combo].leftJoin(query[Combo])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
inline def nodes = quote {
query[App].leftJoin(query[App])
.on((a, b) =>
b.id == a.id &&
b.timestamp > a.timestamp
)
.filter((a, b) =>
b.map(_.id).isEmpty)
.map((a, b) => a)
}
inline def workers = quote {
query[Database].leftJoin(query[Database])
.on((a, b) =>
b.shard == a.shard &&
b.lastTime > a.lastTime
)
.filter((a, b) =>
b.map(_.shard).isEmpty)
.map((a, b) => a)
}
inline def masters = quote {
query[Bridge].leftJoin(query[Bridge])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
inline def masters = quote {
query[Reso].leftJoin(query[Reso])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
inline def masters = quote {
query[Route].leftJoin(query[Route])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
Latest Node Reso
id -mestamp status
1 4 UP
2 5 DOWN
Latest Master Database
id lastCheck state
1 5 WAITING
2 2 WAITING
3 5 FINISHED
Latest Worker Route
shard lastTime reply
1 5 BUSY
2 2 BUSY
3 5 DONE
Latest Node Status
id -mestamp status
1 4 UP
2 5 DOWN
Latest Master Status
id lastCheck state
1 5 WAITING
2 2 WAITING
3 5 FINISHED
Latest Worker Status
shard lastTime reply
1 5 BUSY
2 2 BUSY
3 5 DONE
inline def masters = quote {
query[Master].leftJoin(query[Master])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
inline def nodes = quote {
query[Node].leftJoin(query[Node])
.on((a, b) =>
b.id == a.id &&
b.timestamp > a.timestamp
)
.filter((a, b) =>
b.map(_.id).isEmpty)
.map((a, b) => a)
}
inline def workers = quote {
query[Service].leftJoin(query[Service])
.on((a, b) =>
b.shard == a.shard &&
b.lastTime > a.lastTime
)
.filter((a, b) =>
b.map(_.shard).isEmpty)
.map((a, b) => a)
}
A Practical Example
… you say bo ring, I say be lie vab le !
inline def masters = quote {
query[Master].leftJoin(query[Master])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
inline def masters = quote {
query[Entity].leftJoin(query[Entity])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
inline def masters = quote {
query[Switch].leftJoin(query[Switch])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
Latest Node En-ty
id -mestamp status
1 4 UP
2 5 DOWN
Latest Master Service
id lastCheck state
1 5 WAITING
2 2 WAITING
3 5 FINISHED
Latest Worker Switch
shard lastTime reply
1 5 BUSY
2 2 BUSY
3 5 DONE
Latest Node App
id -mestamp status
1 4 UP
2 5 DOWN
Latest Master Bridge
id lastCheck state
1 5 WAITING
2 2 WAITING
3 5 FINISHED
Latest Worker Combo
shard lastTime reply
1 5 BUSY
2 2 BUSY
3 5 DONE
inline def masters = quote {
query[Combo].leftJoin(query[Combo])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
inline def nodes = quote {
query[App].leftJoin(query[App])
.on((a, b) =>
b.id == a.id &&
b.timestamp > a.timestamp
)
.filter((a, b) =>
b.map(_.id).isEmpty)
.map((a, b) => a)
}
inline def workers = quote {
query[Database].leftJoin(query[Database])
.on((a, b) =>
b.shard == a.shard &&
b.lastTime > a.lastTime
)
.filter((a, b) =>
b.map(_.shard).isEmpty)
.map((a, b) => a)
}
inline def masters = quote {
query[Bridge].leftJoin(query[Bridge])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
inline def masters = quote {
query[Reso].leftJoin(query[Reso])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
inline def masters = quote {
query[Route].leftJoin(query[Route])
.on((a, b) =>
b.key == a.key &&
b.lastCheck > a.lastCheck
)
.filter((a, b) =>
b.map(_.key).isEmpty)
.map((a, b) => a)
}
Latest Node Reso
id -mestamp status
1 4 UP
2 5 DOWN
Latest Master Database
id lastCheck state
1 5 WAITING
2 2 WAITING
3 5 FINISHED
Latest Worker Route
shard lastTime reply
1 5 BUSY
2 2 BUSY
3 5 DONE
inline def workersLatest = quote {
latestStatus(query[Worker])(
w => w.shard,
(a, b) => a.lastTime < b.lastTime)
}
inline def nodesLatest = quote {
latestStatus(query[Node])(
n => n.id,
(a, b) => a.timestamp < b.timestamp)
}
inline def mastersLatest = quote {
latestStatus(query[Master])(
m => m.key,
(a, b) => a.lastCheck < b.lastCheck)
}
Latest Node Status
id -mestam
p
status
1 4 UP
2 5 DOWN
Latest Master Status
id lastCheck state
1 5 WAITING
2 2 WAITING
3 5 FINISHED
Latest Worker Status
id lastTime reply
1 5 BUSY
2 2 BUSY
3 5 DONE
inline def latestStatus[T, G](
inline q: Query[T])(
inline groupKey: T => G,
inline earlierThan: (T, T) => Boolean
): Query[T] =
q.leftJoin(q)
.on((a, b) =>
groupKey(b) == groupKey(a) &&
earlierThan(b, a)
)
.filter((a, b) =>
b.map(b => groupKey(b)).isEmpty)
.map((a, b) => a)
A Practical Example
… you say bo ring, I say be lie vab le !
TypeclassUsecase_Encapsulated.scala
trait GroupKey[T, G]:
inline def apply(inline t: T): G
trait EarlierThan[T]:
inline def apply(inline a: T, inline b: T): Boolean
A Practical Example
… you say bo ring, I say be lie vab le !
TypeclassUsecase_Typeclass.scala
inline given GroupKey[Node, Int]:
inline def apply(inline t: Node): Int = t.id
inline given GroupKey[Master, Int]:
inline def apply(inline t: Master): Int = t.key
inline given GroupKey[Worker, Int]:
inline def apply(inline t: Worker): Int = t.shard
inline given EarlierThan[Node]:
inline def apply(inline a: Node, inline b: Node) = a.timestamp < b.timestamp
inline given EarlierThan[Master]:
inline def apply(inline a: Master, inline b: Master) = a.lastCheck < b.lastCheck
inline given EarlierThan[Worker]:
inline def apply(inline a: Worker, inline b: Worker) = a.lastTime < b.lastTime
TypeclassUsecase_Typeclass.scala
quote { latestStatus(select[Worker]) }
quote { latestStatus(select[Node]) }
quote { latestStatus(select[Master]) }
inline def latestStatus[T, G](
inline q: Query[T])(
using inline groupKey: GroupKey[T, G],
inline earlierThan: EarlierThan[T]
): Query[T] =
q.leftJoin(q)
.on((a, b) =>
groupKey(b) == groupKey(a) &&
earlierThan(b, a)
)
.filter((a, b) =>
b.map(b => groupKey(b)).isEmpty)
.map((a, b) => a)
TypeclassUsecase_Typeclass.scala
quote { latestStatus(select[Worker]) }
quote { latestStatus(select[Node]) }
quote { latestStatus(select[Master]) }
SELECT 

a.id, a.timestamp, a.status

FROM Node a 

LEFT JOIN Node b 

ON b.id = a.id 

AND b.timestamp > a.timestamp 

WHERE b.id IS NULL
SELECT 

a.key, a.lastCheck, a.state

FROM Master a 

LEFT JOIN Master b 

ON b.key = a.key 

AND b.lastCheck > a.lastCheck 

WHERE b.key IS NULL
SELECT 

a.shard, a.lastTime, a.reply 

FROM Worker a 

LEFT JOIN Worker b 

ON b.shard = a.shard 

AND b.lastTime > a.lastTime 

WHERE b.shard IS NULL
inline def latestStatus[T, G](
inline q: Query[T])(
using inline groupKey: GroupKey[T, G],
inline earlierThan: EarlierThan[T]
): Query[T] =
q.leftJoin(q)
.on((a, b) =>
groupKey(b) == groupKey(a) &&
earlierThan(b, a)
)
.filter((a, b) =>
b.map(b => groupKey(b)).isEmpty)
.map((a, b) => a)
TypeclassUsecase_Typeclass.scala
quote { latestStatus(select[Worker]) }
quote { latestStatus(select[Node]) }
quote { latestStatus(select[Master]) }
SELECT 

a.id, a.timestamp, a.status

FROM Node a 

LEFT JOIN Node b 

ON b.id = a.id 

AND b.timestamp > a.timestamp 

WHERE b.id IS NULL
SELECT 

a.key, a.lastCheck, a.state

FROM Master a 

LEFT JOIN Master b 

ON b.key = a.key 

AND b.lastCheck > a.lastCheck 

WHERE b.key IS NULL
SELECT 

a.shard, a.lastTime, a.reply 

FROM Worker a 

LEFT JOIN Worker b 

ON b.shard = a.shard 

AND b.lastTime > a.lastTime 

WHERE b.shard IS NULL
inline def latestStatus[T, G](
inline q: Query[T])(
using inline groupKey: GroupKey[T, G],
inline earlierThan: EarlierThan[T]
): Query[T] =
q.leftJoin(q)
.on((a, b) =>
groupKey(b) == groupKey(a) &&
earlierThan(b, a)
)
.filter((a, b) =>
b.map(b => groupKey(b)).isEmpty)
.map((a, b) => a)
TypeclassUsecase_Typeclass.scala
trait JoiningFunctor[F[_]]:
extension [A, B](inline xs: F[A])
inline def map(inline f: A => B): F[B]
inline def filter(inline f: A => Boolean): F[A]
inline def leftJoin(inline ys: F[B])(inline f: (A, B) => Boolean): F[(A, Option[B])]
Let's Take One More Step
... because we can!
TypeclassUsecase_TypeclassWithList.scala
Now Implement!
Faster! Faster!
inline given JoiningFunctor[Query]:
extension [A, B](inline xs: Query[A])
inline def map(inline f: A => B): Query[B] = xs.map(f)
inline def filter(inline f: A => Boolean): Query[A] = xs.filter(f)
inline def leftJoin(inline ys: Query[B])(inline f: (A, B) => Boolean): Query[(A, Option[B])] =
xs.leftJoin(ys).on(f)
inline given JoiningFunctor[List]:
extension [A, B](inline xs: List[A])
inline def map(inline f: A => B): List[B] = xs.map(f)
inline def filter(inline f: A => Boolean): List[A] = xs.filter(f)
inline def leftJoin(inline ys: List[B])(inline f: (A, B) => Boolean): List[(A, Option[B])] =
xs.flatMap { x =>
val matching = ys.filter(y => f(x, y)).map(y => (x, Some(y)))
if (matching.length == 0) List((x, None)) else matching
}
TypeclassUsecase_TypeclassWithList.scala
With a Query! With a List!
Believe me now?
inline def latestStatus[F[_], T, G](inline q: F[T])(
using inline fun: JoiningFunctor[F],
inline groupKey: GroupKey[T, G],
inline earlierThan: EarlierThan[T]): F[T] =
q.leftJoin(q)((a, b) =>
groupKey(b) == groupKey(a) &&
earlierThan(b, a)
)
.filter((a, b) =>
b.map(b => groupKey(b)).isEmpty)
.map((a, b) => a)
quote {
latestStatus(select[Node])
}
SELECT 

a.id, a.timestamp, a.status

FROM Node a 

LEFT JOIN Node b 

ON b.id = a.id 

AND b.timestamp > a.timestamp 

WHERE b.id IS NULL
val nodesList = List(
Node(1, 1, "UP"),
Node(1, 2, "DOWN"),
Node(2, 3, “WAITING")
)
println( latestStatus(nodesList) )
List(Node(1,1,UP), Node(2,3,WAITING))
TypeclassUsecase_TypeclassWithList.scala
Inline Type-Level Programming
SE RI O USLY…W E’RE DOING THIS!
Every Security DB in the Universe…
Actually it’s…
Actually it’s…
SELECT
s.id, s.name,
r.id, r.name
FROM
User s
INNER JOIN UserToRole so ON so.userId = s.id
INNER JOIN Role r ON r.id = so.roleId
SELECT
s.id, s.name,
r.id, r.name
FROM
User s
INNER JOIN UserToRole so ON so.userId = s.id
INNER JOIN Role r ON r.id = so.roleId
for {
s <- query[User]
sr <- query[UserToRole].join(sr => sr.userId == s.id)
r <- query[Role].join(r => r.id == sr.roleId)
} yield (s, r)
SELECT
s.id, s.name,
r.id, r.name,
p.id, p.name
FROM
User s
INNER JOIN UserToRole so ON so.userId = s.id
INNER JOIN Role r ON r.id = so.roleId
INNER JOIN RoleToPermission rp ON rp.roleId = r.id
INNER JOIN Permission p ON p.id = rp.roleId
SELECT
s.id, s.name,
r.id, r.name,
p.id, p.name
FROM
User s
INNER JOIN UserToRole so ON so.userId = s.id
INNER JOIN Role r ON r.id = so.roleId
INNER JOIN RoleToPermission rp ON rp.roleId = r.id
INNER JOIN Permission p ON p.id = rp.roleId
for {
u <- query[User]
ur <- query[UserToRole].join(so => so.userId == u.id)
r <- query[Role].join(r => r.id == ur.roleId)
rp <- query[RoleToPermission].join(rp => rp.roleId == r.id)
p <- query[Permission].join(p => p.id == rp.roleId)
} yield (u, r, p)
86
… but usually, there’s other stuff
Other Criteria Other JoinsC J
CORE
AGGREGATE
SELECT
s.id, s.name,
r.id, r.name,
p.id, p.name
FROM
User s
INNER JOIN UserToRole so ON so.userId = s.id
INNER JOIN Role r ON r.id = so.roleId
INNER JOIN RoleToPermission rp ON rp.roleId = r.id
INNER JOIN Permission p ON p.id = rp.roleId
87
SELECT
u.id, u.name, r.id, r.name, p.id, p.name, p.venueId, v.id, v.name
FROM
User u
INNER JOIN UserToRole so ON so.userId = u.id
INNER JOIN Role r ON r.id = so.roleId
INNER JOIN RoleToPermission rp ON rp.roleId = r.id
INNER JOIN Permission p ON p.id = rp.roleId
INNER JOIN Venue v ON v.id = p.venueId
WHERE
u.name = 'Joe' AND r.name = 'Drinker' AND p.name = 'Drink' AND v.name = 'Divebar'
88
for {
u <- query[User] if (u.name == "Joe")
ur <- query[UserToRole].join(so => so.userId == u.id)
r <- query[Role].join(r => r.id == ur.roleId) if (r.name == "Drinker")
rp <- query[RoleToPermission].join(rp => rp.roleId == r.id)
p <- query[Permission].join(p => p.id == rp.roleId)
v <- query[Venue].join(v => v.id == p.venueId) if (v.name == "Divebar")
} yield (u, r, p, v)
89
for {
u <- query[User] if (u.name == "Joe")
ur <- query[UserToRole].join(so => so.userId == u.id)
r <- query[Role].join(r => r.id == ur.roleId) if (r.name == "Drinker")
rp <- query[RoleToPermission].join(rp => rp.roleId == r.id)
p <- query[Permission].join(p => p.id == rp.roleId)
v <- query[Venue].join(v => v.id == p.venueId) if (v.name == "Divebar")
} yield (u, r, p, v)
90
Bring on the API Calls!…
Sanit y Not Inclu de d
Is there a better way
to do this???🤔
inline def userAndRoleAndPermissionAndVenue =
quote { ... } => Q[(User,Role,Permission,Venue)]🤪
val userAndRoleAndPermissionAndVenue = quote {
for {
u <- query[User]
ur <- query[UserToRole].join(so => so.userId == u.id)
r <- query[Role].join(r => r.id == ur.roleId)
rp <- query[RoleToPermission].join(rp => rp.roleId == r.id)
p <- query[Permission].join(p => p.id == rp.roleId)
v <- query[Venue].join(v => v.id == p.venueId)
} yield (u, r, p, v)
}
inline def userAndRoleAndPermission =
quote { ... } => Query[(User,Role,Permission)]😕
val userAndRoleAndPermission = quote {
for {
u <- query[User]
ur <- query[UserToRole].join(so => so.userId == u.id)
r <- query[Role].join(r => r.id == ur.roleId)
rp <- query[RoleToPermission].join(rp => rp.roleId == r.id)
p <- query[Permission].join(p => p.id == rp.roleId)
} yield (u, r, p)
}
inline def userAndRole =
quote { ... } => Query[(User,Role)]🙂
val userAndRole = quote {
for {
u <- query[User]
ur <- query[UserToRole].join(so => so.userId == u.id)
r <- query[Role].join(r => r.id == ur.roleId)
} yield (u, r, p)
}
91
Type-Level It!
WHEN THE ALTERNATIVE IS PURE MADNESS…
92
trait Path[From, To]:
type Out
inline def get: Out
Magic Is Here!
Type-Level It!
WHEN THE ALTERNATIVE IS PURE MADNESS…
TypelevelUsecase.scala
93
Type - Level It!
WHEN ALL ELSE FAILS…
trait Path[User, Role]:
type Out = Query[(User, Role)]
trait Path[User, Permission]:
type Out = Query[(User, Role, Permission)]
trait Path[User, Venue]:
type Out = Query[(User, Role, Permission, Venue)]
TypelevelUsecase.scala
94
inline given Path[User, Role]:
type Out = Query[(User, Role)]
inline def get: Query[(User, Role)] =
for {
u <- query[User]
ur <- query[UserToRole].join(ur => ur.userId == s.id)
r <- query[Role].join(r => r.id == ur.roleId)
} yield (u, r)
inline given Path[User, Permission]:
type Out = Query[(User, Role, Permission)]
inline def get: Query[(User, Role, Permission)] =
for {
u <- query[User]
ur <- query[UserToRole].join(ur => ur.userId == s.id)
r <- query[Role].join(r => r.id == ur.roleId)
rp <- query[RoleToPermission].join(rp => rp.roleId == r.id)
p <- query[Permission].join(p => p.id == rp.roleId)
} yield (u, r, p)
inline def path[F, T](using inline path: Path[F, T]): path.Out = path.get
TypelevelUsecase.scala
95
Type - Level It!
WHEN ALL ELSE FAILS…
path[User,Role]: Query[(User, Role)]
path[User,Permission]: Query[(User, Role, Permission)]
path[User, Venue]: Query[(User, Role, Permission, Venue)]
TypelevelUsecase.scala
NOTE: This Slide was skipped
in the talk to save time.
96
inline def q: Quoted[Query[(User, Role)]] =
quote {
path[User,Role]
}
inline def q: Quoted[Query[(User, Role, Permission)]] =
quote {
path[User,Permission]
}
inline def q: Quoted[Query[(User, Role, Permission, Venue)]] =
quote {
path[User,Venue]
}
Type - Level It!
WHEN ALL ELSE FAILS…
TypelevelUsecase.scala
97
inline def q: Quoted[Query[(User, Role)]] =
quote {
path[User,Role]
}
inline def q: Quoted[Query[(User, Role, Permission)]] =
quote {
path[User,Permission]
}
inline def q: Quoted[Query[(User, Role, Permission, Venue)]] =
quote {
path[User,Venue]
}
TypelevelUsecase.scala
val userAndRoleAndPermissionAndVenue = quote {
for {
u <- query[User]
ur <- query[UserToRole].join(so => so.userId == u.id)
r <- query[Role].join(r => r.id == ur.roleId)
rp <- query[RoleToPermission].join(rp => rp.roleId == r.id)
p <- query[Permission].join(p => p.id == rp.roleId)
v <- query[Venue].join(v => v.id == p.venueId)
} yield (u, r, p, v)
}
val userAndRoleAndPermission = quote {
for {
u <- query[User]
ur <- query[UserToRole].join(so => so.userId == u.id)
r <- query[Role].join(r => r.id == ur.roleId)
rp <- query[RoleToPermission].join(rp => rp.roleId == r.id)
p <- query[Permission].join(p => p.id == rp.roleId)
} yield (u, r, p)
}
val userAndRole = quote {
for {
u <- query[User]
ur <- query[UserToRole].join(so => so.userId == u.id)
r <- query[Role].join(r => r.id == ur.roleId)
} yield (u, r, p)
}
inline def q =
quote {
path[User,Venue]
.filter(t => t._1.name == "Joe" && t._2.name == "Drinker" && t._3.name == "Drink"
&& t._4.name == "Divebar")
}
98inline def q: Quoted[Query[(User, Role)]] =
quote {
path[User,Role]
.filter(t => t._1.name == "Joe" && t._2.name == "Drinker")
}
inline def q: Quoted[Query[(User, Role, Permission)]] =
quote {
path[User,Permission]
.filter(t => t._1.name == "Joe" && t._2.name == "Drinker" && t._3.name == "Drink")
}
TypelevelUsecase.scala
NOTE: This Slide was skipped
in the talk to save time.
inline def q: Quoted[Query[(User, Role, Permission, Venue)]] =
quote {
path[User,Venue]
.filter(t => t._1.name == "Joe" && t._2.name == "Drinker" && t._3.name == "Drink"
&& t._4.name == "Divebar")
}
99
SELECT
u.id, u.name, r.id, r.name, p.id, p.name, p.venueId, v.id, v.name
FROM
User u
INNER JOIN UserToRole so ON so.userId = u.id
INNER JOIN Role r ON r.id = so.roleId
INNER JOIN RoleToPermission rp ON rp.roleId = r.id
INNER JOIN Permission p ON p.id = rp.roleId
INNER JOIN Venue v ON v.id = p.venueId
WHERE
u.name = 'Joe' AND r.name = 'Drinker' AND p.name = 'Drink' AND v.name = 'Divebar'
TypelevelUsecase.scala
NOTE: This Slide was skipped
in the talk to save time.
inline def q =
quote {
path[User,Venue].filter { case (u, r, p, v) =>
u.name == "Joe" && r.name == "Drinker" && p.name == "Drink" && v.name == "Divebar"
}
}
100
SELECT
u.id, u.name, r.id, r.name, p.id, p.name, p.venueId, v.id, v.name
FROM
User u
INNER JOIN UserToRole so ON so.userId = u.id
INNER JOIN Role r ON r.id = so.roleId
INNER JOIN RoleToPermission rp ON rp.roleId = r.id
INNER JOIN Permission p ON p.id = rp.roleId
INNER JOIN Venue v ON v.id = p.venueId
WHERE
u.name = 'Joe' AND r.name = 'Drinker' AND p.name = 'Drink' AND v.name = 'Divebar'
TypelevelUsecase.scala
NOTE: This Slide was skipped
in the talk to save time.
inline def q =
quote {
path[User,Venue].filter((u, r, p, v) =>
u.name == "Joe" && r.name == "Drinker" && p.name == "Drink" && v.name == "Divebar"
)
}
101
SELECT
u.id, u.name, r.id, r.name, p.id, p.name, p.venueId, v.id, v.name
FROM
User u
INNER JOIN UserToRole so ON so.userId = u.id
INNER JOIN Role r ON r.id = so.roleId
INNER JOIN RoleToPermission rp ON rp.roleId = r.id
INNER JOIN Permission p ON p.id = rp.roleId
INNER JOIN Venue v ON v.id = p.venueId
WHERE
u.name = 'Joe' AND r.name = 'Drinker' AND p.name = 'Drink' AND v.name = 'Divebar'
TypelevelUsecase.scala
NOTE: This Slide was skipped
in the talk to save time.
inline def q =
quote {
path[User,Venue].filter((u, r, p, v) =>
u.name == "Joe" && r.name == "Drinker" && p.name == "Drink" && v.name == "Divebar"
)
}
run(q)
102
sbt:dotty-simple> compile
SELECT
u.id, u.name, r.id, r.name, p.id, p.name, p.venueId, v.id, v.name
FROM
User u
INNER JOIN UserToRole so ON so.userId = u.id
INNER JOIN Role r ON r.id = so.roleId
INNER JOIN RoleToPermission rp ON rp.roleId = r.id
INNER JOIN Permission p ON p.id = rp.roleId
INNER JOIN Venue v ON v.id = p.venueId
WHERE
u.name = 'Joe' AND r.name = 'Drinker' AND p.name = 'Drink' AND v.name = 'Divebar'
[success] Total time: 7 s, completed Nov 23, 2020 12:16:08 AM
TypelevelUsecase.scala
NOTE: This Slide was skipped
in the talk to save time.
103inline def q =
quote {
path[User,Role]
.filter((u, r) =>
u.name == "Joe" &&
r.name == "Drinker")
}
inline def q = quote {
path[User,Permission]
.filter((u, r, p) =>
u.name == "Joe" &&
r.name == "Drinker" &&
p.name == "Drink")
}
inline def q = quote {
path[User,Venue]
.filter((u, r, p, v) =>
u.name == "Joe" &&
r.name == "Drinker" &&
p.name == "Drink" &&
v.name == "Divebar")
}
SELECT
u.id, u.name, r.id, r.name, p.id, p.name, p.venueId, v.id, v.name
FROM
User u
INNER JOIN UserToRole so ON so.userId = u.id
INNER JOIN Role r ON r.id = so.roleId
INNER JOIN RoleToPermission rp ON rp.roleId = r.id
INNER JOIN Permission p ON p.id = rp.roleId
INNER JOIN Venue v ON v.id = p.venueId
WHERE
u.name = 'Joe' AND r.name = 'Drinker' AND p.name = 'Drink' AND v.name = 'Divebar'
SELECT
u.id, u.name, r.id, r.name, p.id, p.name, p.venueId
FROM
User u
INNER JOIN UserToRole so ON so.userId = u.id
INNER JOIN Role r ON r.id = so.roleId
INNER JOIN RoleToPermission rp ON rp.roleId = r.id
INNER JOIN Permission p ON p.id = rp.roleId
WHERE
u.name = 'Joe' AND r.name = 'Drinker' AND p.name = 'Drink'
SELECT
u.id, u.name, r.id, r.name
FROM
User u
INNER JOIN UserToRole so ON so.userId = u.id
INNER JOIN Role r ON r.id = so.roleId
WHERE
u.name = 'Joe' AND r.name = 'Drinker'
TypelevelUsecase.scala
104
How Far…
Is just that little bit over there?
105
Conclusions
In a good way! It will reveal many new applications
in the years to come.
Inline is the new Implicit
Many new capabilities we've only dreamed about
up to now!
Dotty Enables Quill to be Awesome!

Mais conteúdo relacionado

Mais procurados

Coscup2021 - useful abstractions at rust and it's practical usage
Coscup2021 - useful abstractions at rust and it's practical usageCoscup2021 - useful abstractions at rust and it's practical usage
Coscup2021 - useful abstractions at rust and it's practical usageWayne Tsai
 
Poor Man's Functional Programming
Poor Man's Functional ProgrammingPoor Man's Functional Programming
Poor Man's Functional ProgrammingDmitry Buzdin
 
TDC218SP | Trilha Kotlin - DSLs in a Kotlin Way
TDC218SP | Trilha Kotlin - DSLs in a Kotlin WayTDC218SP | Trilha Kotlin - DSLs in a Kotlin Way
TDC218SP | Trilha Kotlin - DSLs in a Kotlin Waytdc-globalcode
 
Using Scala Slick at FortyTwo
Using Scala Slick at FortyTwoUsing Scala Slick at FortyTwo
Using Scala Slick at FortyTwoEishay Smith
 
The Ring programming language version 1.10 book - Part 22 of 212
The Ring programming language version 1.10 book - Part 22 of 212The Ring programming language version 1.10 book - Part 22 of 212
The Ring programming language version 1.10 book - Part 22 of 212Mahmoud Samir Fayed
 
Functional Programming from OO perspective (Sayeret Lambda lecture)
Functional Programming from OO perspective (Sayeret Lambda lecture)Functional Programming from OO perspective (Sayeret Lambda lecture)
Functional Programming from OO perspective (Sayeret Lambda lecture)Ittay Dror
 
The Ring programming language version 1.5.1 book - Part 36 of 180
The Ring programming language version 1.5.1 book - Part 36 of 180The Ring programming language version 1.5.1 book - Part 36 of 180
The Ring programming language version 1.5.1 book - Part 36 of 180Mahmoud Samir Fayed
 
The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196Mahmoud Samir Fayed
 
Tuga it 2016 - What's New In C# 6
Tuga it 2016 - What's New In C# 6Tuga it 2016 - What's New In C# 6
Tuga it 2016 - What's New In C# 6Paulo Morgado
 
The Ring programming language version 1.10 book - Part 39 of 212
The Ring programming language version 1.10 book - Part 39 of 212The Ring programming language version 1.10 book - Part 39 of 212
The Ring programming language version 1.10 book - Part 39 of 212Mahmoud Samir Fayed
 
Idiomatic Kotlin
Idiomatic KotlinIdiomatic Kotlin
Idiomatic Kotlinintelliyole
 
미려한 UI/UX를 위한 여정
미려한 UI/UX를 위한 여정미려한 UI/UX를 위한 여정
미려한 UI/UX를 위한 여정SeungChul Kang
 
Clojure: The Art of Abstraction
Clojure: The Art of AbstractionClojure: The Art of Abstraction
Clojure: The Art of AbstractionAlex Miller
 
[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵
[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵
[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵Wanbok Choi
 
What's new in C# 6 - NetPonto Porto 20160116
What's new in C# 6  - NetPonto Porto 20160116What's new in C# 6  - NetPonto Porto 20160116
What's new in C# 6 - NetPonto Porto 20160116Paulo Morgado
 
The Ring programming language version 1.5 book - Part 3 of 31
The Ring programming language version 1.5 book - Part 3 of 31The Ring programming language version 1.5 book - Part 3 of 31
The Ring programming language version 1.5 book - Part 3 of 31Mahmoud Samir Fayed
 
The Ring programming language version 1.8 book - Part 39 of 202
The Ring programming language version 1.8 book - Part 39 of 202The Ring programming language version 1.8 book - Part 39 of 202
The Ring programming language version 1.8 book - Part 39 of 202Mahmoud Samir Fayed
 
The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31Mahmoud Samir Fayed
 

Mais procurados (20)

Coscup2021 - useful abstractions at rust and it's practical usage
Coscup2021 - useful abstractions at rust and it's practical usageCoscup2021 - useful abstractions at rust and it's practical usage
Coscup2021 - useful abstractions at rust and it's practical usage
 
Poor Man's Functional Programming
Poor Man's Functional ProgrammingPoor Man's Functional Programming
Poor Man's Functional Programming
 
360|iDev
360|iDev360|iDev
360|iDev
 
TDC218SP | Trilha Kotlin - DSLs in a Kotlin Way
TDC218SP | Trilha Kotlin - DSLs in a Kotlin WayTDC218SP | Trilha Kotlin - DSLs in a Kotlin Way
TDC218SP | Trilha Kotlin - DSLs in a Kotlin Way
 
mobl
moblmobl
mobl
 
Using Scala Slick at FortyTwo
Using Scala Slick at FortyTwoUsing Scala Slick at FortyTwo
Using Scala Slick at FortyTwo
 
The Ring programming language version 1.10 book - Part 22 of 212
The Ring programming language version 1.10 book - Part 22 of 212The Ring programming language version 1.10 book - Part 22 of 212
The Ring programming language version 1.10 book - Part 22 of 212
 
Functional Programming from OO perspective (Sayeret Lambda lecture)
Functional Programming from OO perspective (Sayeret Lambda lecture)Functional Programming from OO perspective (Sayeret Lambda lecture)
Functional Programming from OO perspective (Sayeret Lambda lecture)
 
The Ring programming language version 1.5.1 book - Part 36 of 180
The Ring programming language version 1.5.1 book - Part 36 of 180The Ring programming language version 1.5.1 book - Part 36 of 180
The Ring programming language version 1.5.1 book - Part 36 of 180
 
The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196
 
Tuga it 2016 - What's New In C# 6
Tuga it 2016 - What's New In C# 6Tuga it 2016 - What's New In C# 6
Tuga it 2016 - What's New In C# 6
 
The Ring programming language version 1.10 book - Part 39 of 212
The Ring programming language version 1.10 book - Part 39 of 212The Ring programming language version 1.10 book - Part 39 of 212
The Ring programming language version 1.10 book - Part 39 of 212
 
Idiomatic Kotlin
Idiomatic KotlinIdiomatic Kotlin
Idiomatic Kotlin
 
미려한 UI/UX를 위한 여정
미려한 UI/UX를 위한 여정미려한 UI/UX를 위한 여정
미려한 UI/UX를 위한 여정
 
Clojure: The Art of Abstraction
Clojure: The Art of AbstractionClojure: The Art of Abstraction
Clojure: The Art of Abstraction
 
[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵
[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵
[Let'Swift 2019] 실용적인 함수형 프로그래밍 워크샵
 
What's new in C# 6 - NetPonto Porto 20160116
What's new in C# 6  - NetPonto Porto 20160116What's new in C# 6  - NetPonto Porto 20160116
What's new in C# 6 - NetPonto Porto 20160116
 
The Ring programming language version 1.5 book - Part 3 of 31
The Ring programming language version 1.5 book - Part 3 of 31The Ring programming language version 1.5 book - Part 3 of 31
The Ring programming language version 1.5 book - Part 3 of 31
 
The Ring programming language version 1.8 book - Part 39 of 202
The Ring programming language version 1.8 book - Part 39 of 202The Ring programming language version 1.8 book - Part 39 of 202
The Ring programming language version 1.8 book - Part 39 of 202
 
The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31
 

Semelhante a Functional Scala 2020

Groovy Ast Transformations (greach)
Groovy Ast Transformations (greach)Groovy Ast Transformations (greach)
Groovy Ast Transformations (greach)HamletDRC
 
Kotlin / Android Update
Kotlin / Android UpdateKotlin / Android Update
Kotlin / Android UpdateGarth Gilmour
 
The Ring programming language version 1.5.1 book - Part 32 of 180
The Ring programming language version 1.5.1 book - Part 32 of 180The Ring programming language version 1.5.1 book - Part 32 of 180
The Ring programming language version 1.5.1 book - Part 32 of 180Mahmoud Samir Fayed
 
JDays Lviv 2014: Java8 vs Scala: Difference points & innovation stream
JDays Lviv 2014:  Java8 vs Scala:  Difference points & innovation streamJDays Lviv 2014:  Java8 vs Scala:  Difference points & innovation stream
JDays Lviv 2014: Java8 vs Scala: Difference points & innovation streamRuslan Shevchenko
 
AST Transformations
AST TransformationsAST Transformations
AST TransformationsHamletDRC
 
Go ahead, make my day
Go ahead, make my dayGo ahead, make my day
Go ahead, make my dayTor Ivry
 
Kotlin Developer Starter in Android projects
Kotlin Developer Starter in Android projectsKotlin Developer Starter in Android projects
Kotlin Developer Starter in Android projectsBartosz Kosarzycki
 
Kotlin Developer Starter in Android - STX Next Lightning Talks - Feb 12, 2016
Kotlin Developer Starter in Android - STX Next Lightning Talks - Feb 12, 2016Kotlin Developer Starter in Android - STX Next Lightning Talks - Feb 12, 2016
Kotlin Developer Starter in Android - STX Next Lightning Talks - Feb 12, 2016STX Next
 
Spark Schema For Free with David Szakallas
 Spark Schema For Free with David Szakallas Spark Schema For Free with David Szakallas
Spark Schema For Free with David SzakallasDatabricks
 
AST Transformations at JFokus
AST Transformations at JFokusAST Transformations at JFokus
AST Transformations at JFokusHamletDRC
 
Privet Kotlin (Windy City DevFest)
Privet Kotlin (Windy City DevFest)Privet Kotlin (Windy City DevFest)
Privet Kotlin (Windy City DevFest)Cody Engel
 
The Ring programming language version 1.5.2 book - Part 33 of 181
The Ring programming language version 1.5.2 book - Part 33 of 181The Ring programming language version 1.5.2 book - Part 33 of 181
The Ring programming language version 1.5.2 book - Part 33 of 181Mahmoud Samir Fayed
 
The Ring programming language version 1.5.4 book - Part 40 of 185
The Ring programming language version 1.5.4 book - Part 40 of 185The Ring programming language version 1.5.4 book - Part 40 of 185
The Ring programming language version 1.5.4 book - Part 40 of 185Mahmoud Samir Fayed
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기Arawn Park
 
From java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFrom java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFabio Collini
 

Semelhante a Functional Scala 2020 (20)

Groovy Ast Transformations (greach)
Groovy Ast Transformations (greach)Groovy Ast Transformations (greach)
Groovy Ast Transformations (greach)
 
Kotlin / Android Update
Kotlin / Android UpdateKotlin / Android Update
Kotlin / Android Update
 
A bit about Scala
A bit about ScalaA bit about Scala
A bit about Scala
 
The Ring programming language version 1.5.1 book - Part 32 of 180
The Ring programming language version 1.5.1 book - Part 32 of 180The Ring programming language version 1.5.1 book - Part 32 of 180
The Ring programming language version 1.5.1 book - Part 32 of 180
 
JDays Lviv 2014: Java8 vs Scala: Difference points & innovation stream
JDays Lviv 2014:  Java8 vs Scala:  Difference points & innovation streamJDays Lviv 2014:  Java8 vs Scala:  Difference points & innovation stream
JDays Lviv 2014: Java8 vs Scala: Difference points & innovation stream
 
AST Transformations
AST TransformationsAST Transformations
AST Transformations
 
Go ahead, make my day
Go ahead, make my dayGo ahead, make my day
Go ahead, make my day
 
Kotlin Developer Starter in Android projects
Kotlin Developer Starter in Android projectsKotlin Developer Starter in Android projects
Kotlin Developer Starter in Android projects
 
Kotlin Developer Starter in Android - STX Next Lightning Talks - Feb 12, 2016
Kotlin Developer Starter in Android - STX Next Lightning Talks - Feb 12, 2016Kotlin Developer Starter in Android - STX Next Lightning Talks - Feb 12, 2016
Kotlin Developer Starter in Android - STX Next Lightning Talks - Feb 12, 2016
 
Spark Schema For Free with David Szakallas
 Spark Schema For Free with David Szakallas Spark Schema For Free with David Szakallas
Spark Schema For Free with David Szakallas
 
AST Transformations at JFokus
AST Transformations at JFokusAST Transformations at JFokus
AST Transformations at JFokus
 
Privet Kotlin (Windy City DevFest)
Privet Kotlin (Windy City DevFest)Privet Kotlin (Windy City DevFest)
Privet Kotlin (Windy City DevFest)
 
The Ring programming language version 1.5.2 book - Part 33 of 181
The Ring programming language version 1.5.2 book - Part 33 of 181The Ring programming language version 1.5.2 book - Part 33 of 181
The Ring programming language version 1.5.2 book - Part 33 of 181
 
The Ring programming language version 1.5.4 book - Part 40 of 185
The Ring programming language version 1.5.4 book - Part 40 of 185The Ring programming language version 1.5.4 book - Part 40 of 185
The Ring programming language version 1.5.4 book - Part 40 of 185
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기
 
From java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFrom java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+k
 

Último

Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsArshad QA
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...SelfMade bd
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrandmasabamasaba
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2
 
Harnessing ChatGPT - Elevating Productivity in Today's Agile Environment
Harnessing ChatGPT  - Elevating Productivity in Today's Agile EnvironmentHarnessing ChatGPT  - Elevating Productivity in Today's Agile Environment
Harnessing ChatGPT - Elevating Productivity in Today's Agile EnvironmentVictorSzoltysek
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfonteinmasabamasaba
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park masabamasaba
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Hararemasabamasaba
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024VictoriaMetrics
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...panagenda
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfonteinmasabamasaba
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is insideshinachiaurasa2
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Bert Jan Schrijver
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...masabamasaba
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyviewmasabamasaba
 
tonesoftg
tonesoftgtonesoftg
tonesoftglanshi9
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareJim McKeeth
 

Último (20)

Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
 
Harnessing ChatGPT - Elevating Productivity in Today's Agile Environment
Harnessing ChatGPT  - Elevating Productivity in Today's Agile EnvironmentHarnessing ChatGPT  - Elevating Productivity in Today's Agile Environment
Harnessing ChatGPT - Elevating Productivity in Today's Agile Environment
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 

Functional Scala 2020

  • 1. 1 , DOTTY, AND THE AWESOME POWER OF 'inline' Alexander Ioffe
 @deusaquilus
 https://github.com/deusaquilus/dotty_test/tree/fs_2020_talk/
  • 2. 2 Title One What is Quill? val q = quote { query[Person].filter(p => p.name == "Joe") } sbt:my-project> compile [info] Compiling 1 Scala source to /Users... SELECT p.name, p.age FROM Person p WHERE p.name = 'Joe' case class Person(name: String, age: Int)
  • 3. 3 Title One What is Quill... val q = quote { select[Person].filter(p => p.name == "Joe") } sbt:my-project> compile [info] Compiling 1 Scala source to /Users... SELECT p.name, p.age FROM Person p WHERE p.name = 'Joe' case class Person(name: String, age: Int)
  • 4. 4 Scala Compiler Query Compiler Query[T]AST SELECT … FROM PERSON Parsing Q uery String Your Application Query CompilerAST SELECT … FROM PERSON Parsing Q uery String JDBC Quoted Query[T] Quoted T Result Your Application JDBCT Result Total Runtime
  • 5. 5 Then Your DBA Asks… In a fit of Desperation at 3:00 AM
  • 6. 6 Bring on the Complexity… sbt:my-project> compile [info] Compiling 1 Scala source to /Users... SELECT o1.id, o1.name, o1.population, c1.id, c1.name, c1.countryId, p1.id, p1.name, p1.age, p1.companyId, af.host, af.of, p2.id, p2.name, p2.age, p2.companyId FROM Country o1 INNER JOIN Company c1 ON c1.countryId = o1.id INNER JOIN Person p1 ON p1.companyId = c1.id LEFT JOIN ClientAffiliation af ON af.of = p1.id LEFT JOIN Person p2 ON af.host = p2.id val q = quote { for { o1 <- query[Country] c1 <- query[Company].join(c1 => c1.countryId == o1.id) p1 <- query[Person].join(p1 => p1.companyId == c1.id) af <- query[ClientAffiliation].leftJoin(af => af.of == p1.id) p2 <- query[Person].leftJoin(p2 => af.map(_.host).exists(v => v == p2.id)) } yield (o1, c1, p2, af, p2) } run(q)
  • 7. 7 Limitation - Must Lift Runtime Values val q = quote { query[Person].filter(p => p.name == lift(runtimeVar)) } sbt:my-project> compile [info] Compiling 1 Scala source to /Users... SELECT p.name, p.age FROM Person p WHERE p.name = ? case class Person(name: String, age: Int)
  • 8. 8 Title One Limitation - Runtime Switches val q = if (someRuntimeCondition) quote { query[Person].filter(p => p.name == "Joe") } else quote { query[Person] } sbt:my-project> compile [info] Compiling 1 Scala source to /Users... Dynamic Query case class Person(name: String, age: Int)
  • 9. 9 Limitation - Dynamic Filters sbt:my-project> compile [info] Compiling 1 Scala source to /Users... Dynamic Query case class Person(name: String, age: Int) val filters = Map("name" -> "Joe", "age" -> "22") val q = quote { query[Person].filter(p => col(p, "name") == lift(filters("name")) AND col(p, "age") == lift(filters("age")) ) }
  • 10. 10 Title One Limitation - Encapsulating Methods val q = quote { joes(query[Person]) } sbt:my-project> compile [info] Compiling 1 Scala source to /Users... Dynamic Query case class Person(name: String, age: Int) def joes(q: Query[People]) = q.filter(p => p.name == "Joe")
  • 11. 11 Title One This… val q: Quoted[Query[Person]] = quote { query[Person].filter(p => p.name == "Joe") } sbt:my-project> compile [info] Compiling 1 Scala source to /Users... Dynamic Query case class Person(name: String, age: Int)
  • 12. 12 Title One … Is Really This val q: AnyRef with Quoted[Query[Person]] { def quoted… def lifted… } = quote { query[Person].filter(p => p.name == "Joe") } sbt:my-project> compile [info] Compiling 1 Scala source to /Users... Dynamic Query case class Person(name: String, age: Int)
  • 13. 13 Title One sbt:my-project> compile [info] Compiling 1 Scala source to /Users... Dynamic Query I CAN (EASILY) GET COMPILE-TIME QUERIES … BUT GIVE UP MOST LANGUAGE FEATURES
  • 14. 14
  • 15. 15 Before We Get Started...
  • 16. 16 Title One I was told there would be Macros object Mac { inline def enter(str: String): Unit = ${ enterImpl('str) } def enterImpl(str: Expr[String])(using qctx: QuoteContext): Expr[Unit] = { import qctx.tasty._ '{ () } } } NOTE: This Slide was skipped in the talk to save time.
  • 17. 17 Title One object Mac { inline def enter(str: String): Unit = ${ enterImpl('str) } def enterImpl(str: Expr[String])(using qctx: QuoteContext): Expr[Unit] = { import qctx.tasty._ println(pprint(str.unseal)) '{ () } } } I was told there would be Macros NOTE: This Slide was skipped in the talk to save time.
  • 18. 18 Title One object Mac { inline def enter(str: String): Unit = ${ enterImpl('str) } def enterImpl(str: Expr[String])(using qctx: QuoteContext): Expr[Unit] = { import qctx.tasty._ println(pprint(str.unseal)) '{ () } } } I was told there would be Macros NOTE: This Slide was skipped in the talk to save time.
  • 19. 19 Title One object Mac { inline def enter(str: String): Unit = ${ enterImpl('str) } def enterImpl(str: Expr[String])(using qctx: QuoteContext): Expr[Unit] = { import qctx.tasty._ println(pprint(str.unseal)) '{ () } } } val greeting = "Hello" Mac.enter(greeting) sbt:dotty-simple> compile [info] Compiling 1 Scala source to /Users... Inlined(Thicket(List()), List(), Ident(greeting)) I was told there would be Macros NOTE: This Slide was skipped in the talk to save time.
  • 20. 20 Title One Inline All The Things! object Mac { inline def enter(inline str: String): Unit = ${ enterImpl('str) } def enterImpl(str: Expr[String])(using qctx: QuoteContext): Expr[Unit] = { import qctx.tasty._ println(pprint(str.unseal)) '{ () } } } inline def greeting = "Hello" Mac.enter(greeting) NOTE: This Slide was skipped in the talk to save time.
  • 21. Title One inline def greeting = "Hello" PrintMac(greeting) [info] Compiling 1 Scala source to /Users... Inlined( Thicket(List()), List(), Inlined( Ident(greeting), List(), Literal(("Hello")), ) ) Title One val greeting = "Hello" PrintMac(greeting) [info] Compiling 1 Scala source to /Users... Inlined( Thicket(List()), List(), Ident(greeting) )
  • 22. Inlined( Thicket(List()), List(), Inlined( Ident(combo), List(), Typed( Apply( Select( Inlined(Ident(greeting), List(), Typed(Literal(("Hello")),TypeStuff())), + ), List(Inlined(Ident(suffix),List(),Typed(Literal((" world")),TypeStuff()))) ), TypeStuff() ) ) ) inline def greeting = "Hello" inline def suffix = " world" inline def combo = greeting + suffix PrintMac(combo) Inlined(<originalArg>, <binds>, <expansion>)
  • 23. Inlined( Thicket(List()),List(), Inlined( Ident(combo),List(), Typed( Apply( Select( Apply( Select( Inlined(Ident(greeting),List(),Typed(Literal(("Hello")),TypeStuff())), + ), List(Inlined(Ident(suffix),List(),Typed(Literal((" world")),TypeStuff()))) ), + ), List(Inlined(Ident(suffix2),List(),Typed(Literal((" today!")),TypeStuff()))) ), TypeStuff() ) ) ) inline def greeting = "Hello" inline def suffix = " world" inline def suffix2 = " today!" inline def combo = greeting + suffix + suffix2 Mac.enter(combo) Inlined(<originalArg>, <binds>, <expansion>) NOTE: This Slide was skipped in the talk to save time.
  • 24. def enterImpl(str: Expr[String])(using qctx: QuoteContext): Expr[Unit] = { import qctx.tasty._ println(pprint(str.unseal.underlyingArgument)) '{ () } } def enterImpl(str: Expr[String])(using qctx: QuoteContext): Expr[Unit] = { import qctx.tasty._ println(pprint(str.unseal)) '{ () } } Typed( Literal(("Hello")), TypeStuff() ) Typed( Apply( Select( Apply( Select( Typed(Literal(("Hello")),TypeStuff()), + ), List(Typed(Literal((" world")),TypeStuff())) ), + ), List(Typed(Literal((" today!")),TypeStuff())) ), TypeStuff() ) Inlined(<originalArg>, <binds>, <expansion>) NOTE: This Slide was skipped in the talk to save time.
  • 25. 25 Phase Consistency Principle A.K.A Passing t he Buck… in a go o d way! Splice it In Throw it Out return '{ func($str) } val bar: Expr[Bar] = '{ change($foo) } ∀ pieceOfCode: Expr[T] val str: Expr[T] = … '{ () } Macros Will Always Be Hygienic∴ Transform It Then do one of the former… NOTE: This Slide was skipped in the talk to save time.
  • 26. 26 class Space { class InnerSpace { inline def hello = Mac.passThrough("hello") } inline def world = Mac.passThrough(new InnerSpace().hello) } inline def today = Mac.passThrough(new Space().world + " today") println(today) inline def passThrough(inline str: String): String = ${ passThroughImpl('str) } def passThroughImpl(str: Expr[String])(using qctx: QuoteContext): Expr[String] = { import qctx.tasty._ println(pprint(str.unseal)) str } Phase Consistency Principle A.K.A Passing t he Buck… in a go o d way! This is not available By the time this happens
  • 27. 27 class Space { class InnerSpace { inline def hello = Mac.passThrough("hello") } inline def world = Mac.passThrough(new InnerSpace().hello + " world”) } inline def today = Mac.passThrough(new Space().world + " today") println(today) Phase Consistency Principle A.K.A Passing t he Buck… in a go o d way! Inlined(Thicket(List()),List(),Apply(Select( Inlined(Select(Apply(Select(New(Ident(Space)), <init>), List()), world),List(), Inlined(Apply(Select(Ident(Mac), passThrough),List(Apply(Select( Inlined(Select(Apply(Select(New(Ident(InnerSpace)), <init>), List()), hello),List(), Inlined(Apply(Select(Ident(Mac), passThrough), List(Literal(("hello")))),List(), Inlined(Thicket(List()), List(), Literal(("hello")))), ) ), + ), List(Literal(("world"))))) ), List(), Inlined(Thicket(List()),List(),Apply(Select( Inlined(Select(Apply(Select(New(Ident(InnerSpace)), <init>), List()), hello),List(), Inlined(Apply(Select(Ident(Mac), passThrough), List(Literal(("hello")))),List(), Inlined(Thicket(List()), List(), Literal(("hello")))), ), + ), List(Literal(("world"))))), ), )), + ), List(Literal((" today"))) ) ) Inlined(<originalArg>, <binds>, <expansion>)
  • 28. 28 class Space { class InnerSpace { inline def hello = Mac.passThrough("hello") } inline def world = Mac.passThrough(new InnerSpace().hello + " world”) } inline def today = Mac.passThrough(new Space().world + " today") println(today) Phase Consistency Principle A.K.A Passing t he Buck… in a go o d way! Inlined(Thicket(List()),List(),Apply(Select( Inlined(Select(Apply(Select(New(Ident(Space)), <init>), List()), world),List(), Inlined(Apply(Select(Ident(Mac), passThrough),List(Apply(Select( Inlined(Select(Apply(Select(New(Ident(InnerSpace)), <init>), List()), hello),List(), Inlined(Apply(Select(Ident(Mac), passThrough), List(Literal(("hello")))),List(), Inlined(Thicket(List()), List(), Literal(("hello")))), ) ), + ), List(Literal(("world"))))) ), List(), Inlined(Thicket(List()),List(),Apply(Select( Inlined(Select(Apply(Select(New(Ident(InnerSpace)), <init>), List()), hello),List(), Inlined(Apply(Select(Ident(Mac), passThrough), List(Literal(("hello")))),List(), Inlined(Thicket(List()), List(), Literal(("hello")))), ), + ), List(Literal(("world"))))), ), )), + ), List(Literal((" today"))) ) ) Inlined(<originalArg>, <binds>, <expansion>) Applying In
  • 29. 29 class Space { class InnerSpace { inline def hello = Mac.passThrough("hello") } inline def world = Mac.passThrough(new InnerSpace().hello + " world”) } inline def today = Mac.passThrough(new Space().world + " today") println(today) Phase Consistency Principle A.K.A Passing t he Buck… in a go o d way! Inlined(Thicket(List()),List(),Apply(Select( Inlined(Select(Apply(Select(New(Ident(Space)), <init>), List()), world),List(), Inlined(Apply(Select(Ident(Mac), passThrough),List(Apply(Select( Inlined(Select(Apply(Select(New(Ident(InnerSpace)), <init>), List()), hello),List(), Inlined(Apply(Select(Ident(Mac), passThrough), List(Literal(("hello")))),List(), Inlined(Thicket(List()), List(), Literal(("hello")))), ) ), + ), List(Literal(("world"))))) ), List(), Inlined(Thicket(List()),List(),Apply(Select( Inlined(Select(Apply(Select(New(Ident(InnerSpace)), <init>), List()), hello),List(), Inlined(Apply(Select(Ident(Mac), passThrough), List(Literal(("hello")))),List(), Inlined(Thicket(List()), List(), Literal(("hello")))), ), + ), List(Literal(("world"))))), ), )), + ), List(Literal((" today"))) ) ) Inlined(<originalArg>, <binds>, <expansion>) Applying In Expanding Out
  • 30. 30 class Space { class InnerSpace { inline def hello = Mac.passThrough("hello") } inline def world = Mac.passThrough(new InnerSpace().hello + " world”) } inline def today = Mac.passThrough(new Space().world + " today") println(today) Phase Consistency Principle A.K.A Passing t he Buck… in a go o d way! Inlined(<originalArg>, <binds>, <expansion>) Applying In Expanding Out Built-in Lineage
  • 31. 31 class Space { class InnerSpace { inline def hello = Mac.passThrough("hello") } inline def world = Mac.passThrough(new InnerSpace().hello + " world”) } inline def today = Mac.passThrough(new Space().world + " today") println(today) Phase Consistency Principle A.K.A Passing t he Buck… in a go o d way! Built-in LineageHygienic ⊃
  • 32. 32 In Scala 3 Quill Quotes will be Inline … an d life will b e aweso me ! inline def q = quote { query[Person] } ⊃inline def p = query[Person]
  • 33. 33 inline def q = quote { query[Person] } inline def p = query[Person] ⊃ Inlined(Quoted(EntityQuery[Person] …) Inlined(EntityQuery[Person])
  • 34. 34 inline def q = quote { p } inline def p = query[Person] Inlined(p …) Inlined(EntityQuery[Person])inline def p =
  • 35. 35 inline def q = quote { p } inline def p = query[Person] Inlined(Inlined(EntityQuery[Person]) …) Inlined(EntityQuery[Person])inline def p = The CompilerDoes This!
  • 36. 36 Inlined(Inlined(EntityQuery[Person]) …) Inlined(EntityQuery[Person])inline def p = inline def q = quote { p } inline def p = query[Person] KEEP CALM THROW WHITEBOX MACROS OUT OF A WINDOW
  • 37. 37 Inlined(Inlined(EntityQuery[Person]) …) Inlined(EntityQuery[Person])inline def p = inline def q = quote { p } inline def p = query[Person] All other Language-Constructs must now be compatible with you! All other Language - Constructs must now be compatible with you!
  • 38. 38 inline def q = quote { p } inline def p = query[Person] In Scala 3 Quill Quotes will be Inline … an d life will b e aweso me ! sbt:dotty-simple> compile [info] Compiling 1 Scala source to /Users... SELECT x.name, x.age FROM Person x
  • 39. 39 In Scala 3 Quill Quotes will be Inline … an d life will b e aweso me ! inline def onlyJoes = (p: Person) => p.name == "Joe" inline def q = quote { query[Person].filter(onlyJoes) } List(Person("Joe", 22), Person("Jack", 33)).filter(onlyJoes) [info] running miniquill.MiniExample3_InlineFilter List(Person(Joe,22)) MiniExample3_InlineFilter.scala
  • 40. 40 inline def q = quote { joes(query[Person]) } inline def joes(inline q: Query[Person]) = q.filter(p => p.name == "Joe") sbt:dotty-simple> compile [info] Compiling 1 Scala source to /Users... SELECT x.name, x.age FROM Person x WHERE p.name = 'Joe'
  • 41. 41 inline def joes(inline q: Query[Person], inline filter: Boolean) = inline if (filter) q.filter(p => p.name == "Joe") else q inline def q = quote { joes(query[Person], true) } sbt:dotty-simple> compile [info] Compiling 1 Scala source to /Users... SELECT x.name, x.age FROM Person x WHERE p.name = 'Joe' MiniExample1_Joes.scala
  • 42. 42 inline def joes(inline q: Query[Person], inline filter: Boolean) = inline if (filter) q.filter(p => p.name == "Joe") else q inline def q = quote { joes(query[Person], false) } sbt:dotty-simple> compile [info] Compiling 1 Scala source to /Users... SELECT x.name, x.age FROM Person x MiniExample1_Joes.scala
  • 43. 43 val service = HttpRoutes.of[IO] { case GET -> Root / "people" :? JoesMatcher(isJoe: Boolean) => Ok(stream(joinTables(lift(role), { val q = if (isJoe) quote { query[Person].filter(p => p.name == "Joe") } else quote { query[Person] } run(q) })).transact(xa).map(_.asJson)) val service = HttpRoutes.of[IO] { case GET -> Root / "people" :? JoesMatcher(isJoe: Boolean) => Ok(stream(joinTables(lift(role), { val q1 = quote { query[Person].filter(p => p.name == "Joe") } val q2 = quote { query[Person] } if (isJoe) run(q1) else run(q2) })).transact(xa).map(_.asJson)) [info] Compiling 1 Scala source to /Users Dynamic Query [info] Compiling 1 Scala source to /Users SELECT p.name, p.age FROM Person p WHERE p.name = 'Joe' SELECT p.name, p.age FROM Person p Filters with Http4s get: /app/people?isJoe=true
  • 44. 44 val service = HttpRoutes.of[IO] { case GET -> Root / "people" :? JoesMatcher(isJoe: Boolean) => Ok(stream(joinTables(lift(role), { inline def people(inline isJoeInline: Boolean) = inline if (isJoeInline) query[Person].filter(p => p.name == "Joe") else query[Person] if (isJoe) run(q, true) else run(q, false) })).transact(xa).map(_.asJson)) [info] Compiling 1 Scala source to /Users SELECT p.name, p.age FROM Person p WHERE p.name = 'Joe' SELECT p.name, p.age FROM Person p Filters with Http4s get: /app/people?isJoe=true
  • 45. 45 inline def liftOrAny(inline field: String, inline filter: Option[String]) = lift(filter.getOrElse(null)) == field || lift(filter.getOrElse(null)) == null val runtimeValue = Some("Joe") // or None inline def q = quote { query[Person].filter(p => liftOrAny(p.name, runtimeValue)) } SELECT p.name, p.age FROM Person p WHERE ? = p.name OR ? IS NULL MiniExample2_LiftOrAny.scala Optional Filters
  • 46. 46 inline def liftOrAny(inline field: String, inline filter: Option[String]) = lift(filter.getOrElse(null)) == field || lift(filter.getOrElse(null)) == null val runtimeValue = Some("Joe") inline def q = quote { query[Person].filter(p => liftOrAny(p.name, runtimeValue)) } SELECT p.name, p.age FROM Person p WHERE ? = p.name OR ? IS NULL at runtime… filter Some("Joe") None WHERE 'Joe' = p.name OR 'Joe' IS NULL WHERE null = p.name OR null IS NULL Optional Filters
  • 47. 47 inline def liftOrAny(inline field: String, inline filter: Option[String]) = lift(filter.getOrElse(null)) == field || lift(filter.getOrElse(null)) == null SELECT p.name, p.age FROM Person p WHERE ? = p.name OR ? IS NULL at runtime… filter Some("Joe") None WHERE 'Joe' = p.name OR 'Joe' IS NULL WHERE null = p.name OR null IS NULL val service = HttpRoutes.of[IO] { case GET -> Root / "people" / NameMatcher(nameOpt: Option[String]) => Ok(stream(joinTables(lift(role), { run { query[Person].filter(p => liftOrAny(p.name, nameOpt) } })).transact(xa).map(_.asJson)) get: /app/people or /app/people?name=Joe
  • 48. 48 SELECT p.firstName, p.lastName, p.age FROM Person p WHERE (p.firstName = ? OR ? IS NULL) AND (p.lastName = ? OR ? IS NULL) AND (p.age = ? OR ? IS NULL) AND true extension [T](inline q: EntityQuery[T]): inline def filterByKeys(inline map: Map[String, String]): T = q.filter(p => MapProc[T, PrepareRow](p, map, null, (a, b) => (a == b) || (b == (null) ) )) case class Person(firstName: String, lastName: String, age: Int) val values: Map[String, String] = Map("firstName" -> "Joe", "age" -> "22") inline def q = quote { query[Person].filterByKeys(values) } MiniExample_LiftByKeys.scala
  • 49. 49 SELECT p.firstName, p.lastName, p.age FROM Person p WHERE (p.firstName = values.getOrElse("firstName",null) OR values.getOrElse("firstName",null) IS NULL) AND (p.lastName = values.getOrElse("lastName",null) OR values.getOrElse("lastName",null) IS NULL) AND (p.age = values.getOrElse("age",null) OR values.getOrElse("age",null) IS NULL) AND true case class Person(firstName: String, lastName: String, age: Int) val values: Map[String, String] = Map("firstName" -> "Joe", "age" -> "22") inline def q = quote { query[Person].filterByKeys(values) } MiniExample_LiftByKeys.scala extension [T](inline q: EntityQuery[T]): inline def filterByKeys(inline map: Map[String, String]): T = q.filter(p => MapProc[T, PrepareRow](p, map, null, (a, b) => (a == b) || (b == (null) ) ))
  • 50. 50 SELECT p.firstName, p.lastName, p.age FROM Person p WHERE (p.firstName = 'Joe' OR 'Joe' IS NULL) AND (p.lastName = null OR null IS NULL) AND (p.age = '22' OR '22' IS NULL) AND true case class Person(firstName: String, lastName: String, age: Int) val values: Map[String, String] = Map("firstName" -> "Joe", "age" -> "22") inline def q = quote { query[Person].filterByKeys(values) } MiniExample_LiftByKeys.scala extension [T](inline q: EntityQuery[T]): inline def filterByKeys(inline map: Map[String, String]): T = q.filter(p => MapProc[T, PrepareRow](p, map, null, (a, b) => (a == b) || (b == (null) ) ))
  • 51. 51 SELECT p.firstName, p.lastName, p.age FROM Person p WHERE (p.firstName = 'Joe' OR 'Joe' IS NULL) AND (p.lastName = null OR null IS NULL) AND (p.age = '22' OR '22' IS NULL) AND true get: /app/people?firstName=Joe&age=22 val service = HttpRoutes.of[IO] { case GET -> Root / "people" :? (multiFilters: Map[String, Seq[String]]) => Ok(stream(joinTables(lift(role), { val filters: Map[String, String] = multiFilters.flatMap((k, v) => (k, v.take(1)) val q = quote { query[Person].filterByKeys(filters) } run(q) })).transact(xa).map(_.asJson))
  • 52. 52 inline def oneOf(inline list: List[String], inline column:String): Boolean = { inline if (ListProc.isNil(list)) false else ListProc.index(list, 0) == column || oneOf(ListProc.tail(list), column) } Processing with Recursive Inlines First Curse, then Recurse! inline def q = quote { query[Person].filter(p => oneOf(List("Joe", "Jack"), p.name)) } [info] Compiling 1 Scala source to /Users… SELECT p.name, p.age FROM Person p WHERE 'Joe' = p.name OR 'Jack' = p.name OR false case class Person(name: String, age: Int) MiniExample_OneOf.scala NOTE: This Slide was skipped in the talk to save time.
  • 53. 53 inline def oneOf(inline list: List[String], inline column:String): Boolean = { inline if (ListProc.isNil(list)) false else ListProc.index(list, 0) == column || oneOf(ListProc.tail(list), column) } Processing with Recursive Inlines First Curse, then Recurse! inline def q = quote { query[Person].filter(n => oneOf(List(n.lastStatus, n.backupStatus), p.name)) } SELECT n.name, n.age FROM Node n WHERE n.lastStatus = n.status OR n.backupStatus = n.status OR false case class Node(status: String, lastStatus: String, backupStatus: Int) MiniExample_OneOf.scala NOTE: This Slide was skipped in the talk to save time.
  • 54. 54 inline def oneOf(inline list: List[String], inline column:String): Boolean = { inline if (ListProc.isNil(list)) false else ListProc.index(list, 0) == column || oneOf(ListProc.tail(list), column) } Processing with Recursive Inlines First Curse, then Recurse! inline def q = quote { query[Person].filter(n => oneOf(List(n.lastStatus,"restarting"), n.status)) } SELECT n.name, n.age FROM Node n WHERE n.lastStatus = n.status OR 'restarting' = n.status OR false case class Node(status: String, lastStatus: String, backupStatus: Int) MiniExample_OneOf.scala NOTE: This Slide was skipped in the talk to save time.
  • 55. 55 Inline Type-Classes THERE’S ACTUALLY A GOOD REASON TO DO THIS!
  • 56. NOTE: This Slide was skipped in the talk to save time.
  • 57. trait Functor[F[_]]: inline def map[A, B](inline xs: F[A], inline f: A => B): F[B] class ListFunctor extends Functor[List]: inline def map[A, B](inline xs: List[A], inline f: A => B): List[B] = xs.map(f) class QueryFunctor extends Functor[Query]: inline def map[A, B](inline xs: Query[A], inline f: A => B): Query[B] = xs.map(f) inline given listFunctor as ListFunctor = new ListFunctor inline given queryFunctor as QueryFunctor = new QueryFunctor inline def doMap[F[_], A, B](inline from: F[A], inline f: A => B)(using inline fun: Functor[F]): F[B] = fun.map(from, f) val list2 = doMap(List(1,2,3), (i: Int) => i + 1) // Runtime inline def q = quote { doMap(select[Person], (p: Person) => p.name) } // Compile-Time! An Inline Functor… N o , r e a l l y. T h i s i s a c t u a l l y p o s s i b l e ! [info] Compiling 1 Scala source to /Users... Compile Time Query Is: SELECT p.name FROM Person p runMain miniquill.InlineMacroTest1FunctionalTypeclass List(2, 3, 4) TypeclassExample_FunctorOldStyle.scala NOTE: This Slide was skipped in the talk to save time.
  • 58. An Inline Functor… Dotty Idiomatically! Tell t hem you saw it h ere first! trait Functor[F[_]]: extension [A, B](inline x: F[A]): inline def map(inline f: A => B): F[B] inline given Functor[List]: extension [A, B](inline xs: List[A]) inline def map(inline f: A => B): List[B] = xs.map(f) inline given Functor[Query]: extension [A, B](inline xs: Query[A]) inline def map(inline f: A => B): Query[B] = xs.map(f) extension [F[_], A, B](inline from: F[A])(using inline fun: Functor[F]): inline def mapF(inline f: A => B) = from.map(f) println( List(1,2,3).mapF(i => i + 1) ) inline def q = quote { select[Person].up.mapF(p => p.name) } TypeclassExample_Functor.scala NOTE: This Slide was skipped in the talk to save time.
  • 59. … and a Monad! We l l , n o t r e a l l y a M o n a d s i n c e w e d o n ’ t h a v e P u r e . Actually it ’s th e Flat Map Type cla ss trait Monad[F[_]] extends Functor[F]: extension [A, B](inline x: F[A]): inline def map(inline f: A => B): F[B] inline def flatMap(inline f: A => F[B]): F[B] inline given Monad[List]: extension [A, B](inline xs: List[A]) inline def map(inline f: A => B): List[B] = xs.map(f) inline def flatMap(inline f: A => List[B]): List[B] = xs.flatMap(f) inline given Monad[Query]: extension [A, B](inline xs: Query[A]) inline def map(inline f: A => B): Query[B] = xs.map(f) inline def flatMap(inline f: A => Query[B]): Query[B] = xs.flatMap(f) extension [F[_], A, B](inline from: F[A])(using inline fun: Monad[F]): inline def mapM(inline f: A => B) = from.map(f) inline def flatMapM(inline f: A => F[B]) = from.flatMap(f) inline def q = quote { select[Person].mapF(p => p.name) } inline def q = quote { select[Person].flatMapM(p => query[Address]) } TypeclassExample_Monad.scala NOTE: This Slide was skipped in the talk to save time.
  • 60. trait For[F[_]]: extension [A, B](inline x: F[A]): inline def map(inline f: A => B): F[B] inline def flatMap(inline f: A => F[B]): F[B] inline def withFilter(inline f: A => Boolean): F[A] inline given For[List]: extension [A, B](inline xs: List[A]) inline def map(inline f: A => B): List[B] = xs.map(f) inline def flatMap(inline f: A => List[B]): List[B] = xs.flatMap(f) inline def withFilter(inline f: A => Boolean): List[A] = xs.filter(f) inline given For[Query]: extension [A, B](inline xs: Query[A]) inline def map(inline f: A => B): Query[B] = xs.map(f) inline def flatMap(inline f: A => Query[B]): Query[B] = xs.flatMap(f) inline def withFilter(inline f: A => Boolean): Query[A] = xs.withFilter(f) Comprehend This! … a nd you will be save d! object UseCase: extension [F[_]](inline people: F[Person])(using inline fun: For[F]): inline def joesAddresses(inline addresses: F[Address]) = for { p <- people if (p.name == "Joe") a <- addresses if (p.id == a.fk) } yield (p, a) TypeclassExample_For.scala NOTE: This Slide was skipped in the talk to save time.
  • 61. Comprehend This! … a nd you will be save d! object UseCase: extension [F[_]](inline people: F[Person])(using inline fun: For[F]): inline def joesAddresses(inline addresses: F[Address]) = for { p <- people if (p.name == "Joe") a <- addresses if (p.id == a.fk) } yield (p, a) [info] Compiling 1 Scala source to /Users… SELECT p.id, p.name, p.age, a.fk, a.street, a.zip FROM Person p, Address a WHERE p.name = 'Joe' AND p.id = a.fk val peopleL = List(Person(1, "Joe", 22), Person(2, "Jack", 33), Person(3, "James", 44)) val addressesL = List(Address(1, "123 St.", 111), Address(2, "456 St.", 222), Address(3, "789 St.", 333)) [info] running miniquill.InlineMacroTest1FunctionalTypeclassIdiomatic List((Person(1,Joe,22),Address(1,123 St.,111))) inline def q = quote { people.joesAddresses(addresses) } println(peopleL.joesAddresses(addressesL)) Compile Time Runtime TypeclassExample_For.scala NOTE: This Slide was skipped in the talk to save time.
  • 62.
  • 63. Log Time1 - Node1 Update Time2 - Node2 Update Time3 - Node1 Update Time4 - Node1 Update Time5 - Node2 Update Log Time1 - Master1 Update Time2 - Master2 Update Time3 - Master3 Update Time4 - Master1 Update Time5 - Master3 Update Log Time1 - Node1 Update Time2 - Node3 Update Time3 - Node3 Update Time4 - Node2 Update Time5 - Node2 Update A Practical Example … you say bo ring, I say be lie vab le ! N o d e Master Wo r k e r
  • 64. Latest Node Status id -mestamp status 1 4 UP 2 5 DOWN Latest Master Status id lastCheck state 1 5 WAITING 2 2 WAITING 3 5 FINISHED Latest Worker Status shard lastTime reply 1 5 BUSY 2 2 BUSY 3 5 DONE Node id -mestamp status 1 1 UP 2 2 UP 1 3 DOWN 1 4 UP 2 5 DOWN Master id lastCheck state 1 1 IDLE 2 2 WAITING 3 3 WAITING 1 4 WAITING 3 5 FINISHED Worker shard lastTime reply 1 1 BUSY 3 2 BUSY 3 3 DONE 2 4 DONE 2 5 BUSY A Practical Example … you say bo ring, I say be lie vab le !
  • 65. Latest Node Status id -mestamp status 1 4 UP 2 5 DOWN Latest Master Status id lastCheck state 1 5 WAITING 2 2 WAITING 3 5 FINISHED Latest Worker Status shard lastTime reply 1 5 BUSY 2 2 BUSY 3 5 DONE SELECT a.id, a.timestamp, a.status FROM Node a LEFT JOIN Node b ON b.id = a.id AND b.timestamp > a.timestamp WHERE b.id IS NULL SELECT a.key, a.lastCheck, a.state FROM Master a LEFT JOIN Master b ON b.key = a.key AND b.lastCheck > a.lastCheck WHERE b.key IS NULL SELECT a.shard, a.lastTime, a.reply FROM Worker a LEFT JOIN Worker b ON b.shard = a.shard AND b.lastTime > a.lastTime WHERE b.shard IS NULL A Practical Example … you say bo ring, I say be lie vab le ! TypeclassUsecase_Repetitive.scala
  • 66. Latest Node Status id -mestamp status 1 4 UP 2 5 DOWN Latest Master Status id lastCheck state 1 5 WAITING 2 2 WAITING 3 5 FINISHED Latest Worker Status shard lastTime reply 1 5 BUSY 2 2 BUSY 3 5 DONE inline def masters = quote { query[Master].leftJoin(query[Master]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } inline def nodes = quote { query[Node].leftJoin(query[Node]) .on((a, b) => b.id == a.id && b.timestamp > a.timestamp ) .filter((a, b) => b.map(_.id).isEmpty) .map((a, b) => a) } inline def workers = quote { query[Worker].leftJoin(query[Worker]) .on((a, b) => b.shard == a.shard && b.lastTime > a.lastTime ) .filter((a, b) => b.map(_.shard).isEmpty) .map((a, b) => a) } A Practical Example … you say bo ring, I say be lie vab le !
  • 67. Latest Node Status id -mestamp status 1 4 UP 2 5 DOWN Latest Master Status id lastCheck state 1 5 WAITING 2 2 WAITING 3 5 FINISHED Latest Worker Status shard lastTime reply 1 5 BUSY 2 2 BUSY 3 5 DONE inline def masters = quote { query[Master].leftJoin(query[Master]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } inline def nodes = quote { query[Node].leftJoin(query[Node]) .on((a, b) => b.id == a.id && b.timestamp > a.timestamp ) .filter((a, b) => b.map(_.id).isEmpty) .map((a, b) => a) } inline def workers = quote { query[Service].leftJoin(query[Service]) .on((a, b) => b.shard == a.shard && b.lastTime > a.lastTime ) .filter((a, b) => b.map(_.shard).isEmpty) .map((a, b) => a) } A Practical Example … you say bo ring, I say be lie vab le ! inline def masters = quote { query[Master].leftJoin(query[Master]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } inline def masters = quote { query[Entity].leftJoin(query[Entity]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } inline def masters = quote { query[Switch].leftJoin(query[Switch]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } Latest Node En-ty id -mestamp status 1 4 UP 2 5 DOWN Latest Master Service id lastCheck state 1 5 WAITING 2 2 WAITING 3 5 FINISHED Latest Worker Switch shard lastTime reply 1 5 BUSY 2 2 BUSY 3 5 DONE Latest Node App id -mestamp status 1 4 UP 2 5 DOWN Latest Master Bridge id lastCheck state 1 5 WAITING 2 2 WAITING 3 5 FINISHED Latest Worker Combo shard lastTime reply 1 5 BUSY 2 2 BUSY 3 5 DONE inline def masters = quote { query[Combo].leftJoin(query[Combo]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } inline def nodes = quote { query[App].leftJoin(query[App]) .on((a, b) => b.id == a.id && b.timestamp > a.timestamp ) .filter((a, b) => b.map(_.id).isEmpty) .map((a, b) => a) } inline def workers = quote { query[Database].leftJoin(query[Database]) .on((a, b) => b.shard == a.shard && b.lastTime > a.lastTime ) .filter((a, b) => b.map(_.shard).isEmpty) .map((a, b) => a) } inline def masters = quote { query[Bridge].leftJoin(query[Bridge]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } inline def masters = quote { query[Reso].leftJoin(query[Reso]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } inline def masters = quote { query[Route].leftJoin(query[Route]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } Latest Node Reso id -mestamp status 1 4 UP 2 5 DOWN Latest Master Database id lastCheck state 1 5 WAITING 2 2 WAITING 3 5 FINISHED Latest Worker Route shard lastTime reply 1 5 BUSY 2 2 BUSY 3 5 DONE
  • 68. Latest Node Status id -mestamp status 1 4 UP 2 5 DOWN Latest Master Status id lastCheck state 1 5 WAITING 2 2 WAITING 3 5 FINISHED Latest Worker Status shard lastTime reply 1 5 BUSY 2 2 BUSY 3 5 DONE inline def masters = quote { query[Master].leftJoin(query[Master]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } inline def nodes = quote { query[Node].leftJoin(query[Node]) .on((a, b) => b.id == a.id && b.timestamp > a.timestamp ) .filter((a, b) => b.map(_.id).isEmpty) .map((a, b) => a) } inline def workers = quote { query[Service].leftJoin(query[Service]) .on((a, b) => b.shard == a.shard && b.lastTime > a.lastTime ) .filter((a, b) => b.map(_.shard).isEmpty) .map((a, b) => a) } A Practical Example … you say bo ring, I say be lie vab le ! inline def masters = quote { query[Master].leftJoin(query[Master]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } inline def masters = quote { query[Entity].leftJoin(query[Entity]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } inline def masters = quote { query[Switch].leftJoin(query[Switch]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } Latest Node En-ty id -mestamp status 1 4 UP 2 5 DOWN Latest Master Service id lastCheck state 1 5 WAITING 2 2 WAITING 3 5 FINISHED Latest Worker Switch shard lastTime reply 1 5 BUSY 2 2 BUSY 3 5 DONE Latest Node App id -mestamp status 1 4 UP 2 5 DOWN Latest Master Bridge id lastCheck state 1 5 WAITING 2 2 WAITING 3 5 FINISHED Latest Worker Combo shard lastTime reply 1 5 BUSY 2 2 BUSY 3 5 DONE inline def masters = quote { query[Combo].leftJoin(query[Combo]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } inline def nodes = quote { query[App].leftJoin(query[App]) .on((a, b) => b.id == a.id && b.timestamp > a.timestamp ) .filter((a, b) => b.map(_.id).isEmpty) .map((a, b) => a) } inline def workers = quote { query[Database].leftJoin(query[Database]) .on((a, b) => b.shard == a.shard && b.lastTime > a.lastTime ) .filter((a, b) => b.map(_.shard).isEmpty) .map((a, b) => a) } inline def masters = quote { query[Bridge].leftJoin(query[Bridge]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } inline def masters = quote { query[Reso].leftJoin(query[Reso]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } inline def masters = quote { query[Route].leftJoin(query[Route]) .on((a, b) => b.key == a.key && b.lastCheck > a.lastCheck ) .filter((a, b) => b.map(_.key).isEmpty) .map((a, b) => a) } Latest Node Reso id -mestamp status 1 4 UP 2 5 DOWN Latest Master Database id lastCheck state 1 5 WAITING 2 2 WAITING 3 5 FINISHED Latest Worker Route shard lastTime reply 1 5 BUSY 2 2 BUSY 3 5 DONE
  • 69. inline def workersLatest = quote { latestStatus(query[Worker])( w => w.shard, (a, b) => a.lastTime < b.lastTime) } inline def nodesLatest = quote { latestStatus(query[Node])( n => n.id, (a, b) => a.timestamp < b.timestamp) } inline def mastersLatest = quote { latestStatus(query[Master])( m => m.key, (a, b) => a.lastCheck < b.lastCheck) } Latest Node Status id -mestam p status 1 4 UP 2 5 DOWN Latest Master Status id lastCheck state 1 5 WAITING 2 2 WAITING 3 5 FINISHED Latest Worker Status id lastTime reply 1 5 BUSY 2 2 BUSY 3 5 DONE inline def latestStatus[T, G]( inline q: Query[T])( inline groupKey: T => G, inline earlierThan: (T, T) => Boolean ): Query[T] = q.leftJoin(q) .on((a, b) => groupKey(b) == groupKey(a) && earlierThan(b, a) ) .filter((a, b) => b.map(b => groupKey(b)).isEmpty) .map((a, b) => a) A Practical Example … you say bo ring, I say be lie vab le ! TypeclassUsecase_Encapsulated.scala
  • 70. trait GroupKey[T, G]: inline def apply(inline t: T): G trait EarlierThan[T]: inline def apply(inline a: T, inline b: T): Boolean A Practical Example … you say bo ring, I say be lie vab le ! TypeclassUsecase_Typeclass.scala
  • 71. inline given GroupKey[Node, Int]: inline def apply(inline t: Node): Int = t.id inline given GroupKey[Master, Int]: inline def apply(inline t: Master): Int = t.key inline given GroupKey[Worker, Int]: inline def apply(inline t: Worker): Int = t.shard inline given EarlierThan[Node]: inline def apply(inline a: Node, inline b: Node) = a.timestamp < b.timestamp inline given EarlierThan[Master]: inline def apply(inline a: Master, inline b: Master) = a.lastCheck < b.lastCheck inline given EarlierThan[Worker]: inline def apply(inline a: Worker, inline b: Worker) = a.lastTime < b.lastTime TypeclassUsecase_Typeclass.scala
  • 72. quote { latestStatus(select[Worker]) } quote { latestStatus(select[Node]) } quote { latestStatus(select[Master]) } inline def latestStatus[T, G]( inline q: Query[T])( using inline groupKey: GroupKey[T, G], inline earlierThan: EarlierThan[T] ): Query[T] = q.leftJoin(q) .on((a, b) => groupKey(b) == groupKey(a) && earlierThan(b, a) ) .filter((a, b) => b.map(b => groupKey(b)).isEmpty) .map((a, b) => a) TypeclassUsecase_Typeclass.scala
  • 73. quote { latestStatus(select[Worker]) } quote { latestStatus(select[Node]) } quote { latestStatus(select[Master]) } SELECT a.id, a.timestamp, a.status FROM Node a LEFT JOIN Node b ON b.id = a.id AND b.timestamp > a.timestamp WHERE b.id IS NULL SELECT a.key, a.lastCheck, a.state FROM Master a LEFT JOIN Master b ON b.key = a.key AND b.lastCheck > a.lastCheck WHERE b.key IS NULL SELECT a.shard, a.lastTime, a.reply FROM Worker a LEFT JOIN Worker b ON b.shard = a.shard AND b.lastTime > a.lastTime WHERE b.shard IS NULL inline def latestStatus[T, G]( inline q: Query[T])( using inline groupKey: GroupKey[T, G], inline earlierThan: EarlierThan[T] ): Query[T] = q.leftJoin(q) .on((a, b) => groupKey(b) == groupKey(a) && earlierThan(b, a) ) .filter((a, b) => b.map(b => groupKey(b)).isEmpty) .map((a, b) => a) TypeclassUsecase_Typeclass.scala
  • 74. quote { latestStatus(select[Worker]) } quote { latestStatus(select[Node]) } quote { latestStatus(select[Master]) } SELECT a.id, a.timestamp, a.status FROM Node a LEFT JOIN Node b ON b.id = a.id AND b.timestamp > a.timestamp WHERE b.id IS NULL SELECT a.key, a.lastCheck, a.state FROM Master a LEFT JOIN Master b ON b.key = a.key AND b.lastCheck > a.lastCheck WHERE b.key IS NULL SELECT a.shard, a.lastTime, a.reply FROM Worker a LEFT JOIN Worker b ON b.shard = a.shard AND b.lastTime > a.lastTime WHERE b.shard IS NULL inline def latestStatus[T, G]( inline q: Query[T])( using inline groupKey: GroupKey[T, G], inline earlierThan: EarlierThan[T] ): Query[T] = q.leftJoin(q) .on((a, b) => groupKey(b) == groupKey(a) && earlierThan(b, a) ) .filter((a, b) => b.map(b => groupKey(b)).isEmpty) .map((a, b) => a) TypeclassUsecase_Typeclass.scala
  • 75. trait JoiningFunctor[F[_]]: extension [A, B](inline xs: F[A]) inline def map(inline f: A => B): F[B] inline def filter(inline f: A => Boolean): F[A] inline def leftJoin(inline ys: F[B])(inline f: (A, B) => Boolean): F[(A, Option[B])] Let's Take One More Step ... because we can! TypeclassUsecase_TypeclassWithList.scala
  • 76. Now Implement! Faster! Faster! inline given JoiningFunctor[Query]: extension [A, B](inline xs: Query[A]) inline def map(inline f: A => B): Query[B] = xs.map(f) inline def filter(inline f: A => Boolean): Query[A] = xs.filter(f) inline def leftJoin(inline ys: Query[B])(inline f: (A, B) => Boolean): Query[(A, Option[B])] = xs.leftJoin(ys).on(f) inline given JoiningFunctor[List]: extension [A, B](inline xs: List[A]) inline def map(inline f: A => B): List[B] = xs.map(f) inline def filter(inline f: A => Boolean): List[A] = xs.filter(f) inline def leftJoin(inline ys: List[B])(inline f: (A, B) => Boolean): List[(A, Option[B])] = xs.flatMap { x => val matching = ys.filter(y => f(x, y)).map(y => (x, Some(y))) if (matching.length == 0) List((x, None)) else matching } TypeclassUsecase_TypeclassWithList.scala
  • 77. With a Query! With a List! Believe me now? inline def latestStatus[F[_], T, G](inline q: F[T])( using inline fun: JoiningFunctor[F], inline groupKey: GroupKey[T, G], inline earlierThan: EarlierThan[T]): F[T] = q.leftJoin(q)((a, b) => groupKey(b) == groupKey(a) && earlierThan(b, a) ) .filter((a, b) => b.map(b => groupKey(b)).isEmpty) .map((a, b) => a) quote { latestStatus(select[Node]) } SELECT a.id, a.timestamp, a.status FROM Node a LEFT JOIN Node b ON b.id = a.id AND b.timestamp > a.timestamp WHERE b.id IS NULL val nodesList = List( Node(1, 1, "UP"), Node(1, 2, "DOWN"), Node(2, 3, “WAITING") ) println( latestStatus(nodesList) ) List(Node(1,1,UP), Node(2,3,WAITING)) TypeclassUsecase_TypeclassWithList.scala
  • 78. Inline Type-Level Programming SE RI O USLY…W E’RE DOING THIS!
  • 79. Every Security DB in the Universe…
  • 82. SELECT s.id, s.name, r.id, r.name FROM User s INNER JOIN UserToRole so ON so.userId = s.id INNER JOIN Role r ON r.id = so.roleId
  • 83. SELECT s.id, s.name, r.id, r.name FROM User s INNER JOIN UserToRole so ON so.userId = s.id INNER JOIN Role r ON r.id = so.roleId for { s <- query[User] sr <- query[UserToRole].join(sr => sr.userId == s.id) r <- query[Role].join(r => r.id == sr.roleId) } yield (s, r)
  • 84. SELECT s.id, s.name, r.id, r.name, p.id, p.name FROM User s INNER JOIN UserToRole so ON so.userId = s.id INNER JOIN Role r ON r.id = so.roleId INNER JOIN RoleToPermission rp ON rp.roleId = r.id INNER JOIN Permission p ON p.id = rp.roleId
  • 85. SELECT s.id, s.name, r.id, r.name, p.id, p.name FROM User s INNER JOIN UserToRole so ON so.userId = s.id INNER JOIN Role r ON r.id = so.roleId INNER JOIN RoleToPermission rp ON rp.roleId = r.id INNER JOIN Permission p ON p.id = rp.roleId for { u <- query[User] ur <- query[UserToRole].join(so => so.userId == u.id) r <- query[Role].join(r => r.id == ur.roleId) rp <- query[RoleToPermission].join(rp => rp.roleId == r.id) p <- query[Permission].join(p => p.id == rp.roleId) } yield (u, r, p)
  • 86. 86 … but usually, there’s other stuff Other Criteria Other JoinsC J CORE AGGREGATE SELECT s.id, s.name, r.id, r.name, p.id, p.name FROM User s INNER JOIN UserToRole so ON so.userId = s.id INNER JOIN Role r ON r.id = so.roleId INNER JOIN RoleToPermission rp ON rp.roleId = r.id INNER JOIN Permission p ON p.id = rp.roleId
  • 87. 87 SELECT u.id, u.name, r.id, r.name, p.id, p.name, p.venueId, v.id, v.name FROM User u INNER JOIN UserToRole so ON so.userId = u.id INNER JOIN Role r ON r.id = so.roleId INNER JOIN RoleToPermission rp ON rp.roleId = r.id INNER JOIN Permission p ON p.id = rp.roleId INNER JOIN Venue v ON v.id = p.venueId WHERE u.name = 'Joe' AND r.name = 'Drinker' AND p.name = 'Drink' AND v.name = 'Divebar'
  • 88. 88 for { u <- query[User] if (u.name == "Joe") ur <- query[UserToRole].join(so => so.userId == u.id) r <- query[Role].join(r => r.id == ur.roleId) if (r.name == "Drinker") rp <- query[RoleToPermission].join(rp => rp.roleId == r.id) p <- query[Permission].join(p => p.id == rp.roleId) v <- query[Venue].join(v => v.id == p.venueId) if (v.name == "Divebar") } yield (u, r, p, v)
  • 89. 89 for { u <- query[User] if (u.name == "Joe") ur <- query[UserToRole].join(so => so.userId == u.id) r <- query[Role].join(r => r.id == ur.roleId) if (r.name == "Drinker") rp <- query[RoleToPermission].join(rp => rp.roleId == r.id) p <- query[Permission].join(p => p.id == rp.roleId) v <- query[Venue].join(v => v.id == p.venueId) if (v.name == "Divebar") } yield (u, r, p, v)
  • 90. 90 Bring on the API Calls!… Sanit y Not Inclu de d Is there a better way to do this???🤔 inline def userAndRoleAndPermissionAndVenue = quote { ... } => Q[(User,Role,Permission,Venue)]🤪 val userAndRoleAndPermissionAndVenue = quote { for { u <- query[User] ur <- query[UserToRole].join(so => so.userId == u.id) r <- query[Role].join(r => r.id == ur.roleId) rp <- query[RoleToPermission].join(rp => rp.roleId == r.id) p <- query[Permission].join(p => p.id == rp.roleId) v <- query[Venue].join(v => v.id == p.venueId) } yield (u, r, p, v) } inline def userAndRoleAndPermission = quote { ... } => Query[(User,Role,Permission)]😕 val userAndRoleAndPermission = quote { for { u <- query[User] ur <- query[UserToRole].join(so => so.userId == u.id) r <- query[Role].join(r => r.id == ur.roleId) rp <- query[RoleToPermission].join(rp => rp.roleId == r.id) p <- query[Permission].join(p => p.id == rp.roleId) } yield (u, r, p) } inline def userAndRole = quote { ... } => Query[(User,Role)]🙂 val userAndRole = quote { for { u <- query[User] ur <- query[UserToRole].join(so => so.userId == u.id) r <- query[Role].join(r => r.id == ur.roleId) } yield (u, r, p) }
  • 91. 91 Type-Level It! WHEN THE ALTERNATIVE IS PURE MADNESS…
  • 92. 92 trait Path[From, To]: type Out inline def get: Out Magic Is Here! Type-Level It! WHEN THE ALTERNATIVE IS PURE MADNESS… TypelevelUsecase.scala
  • 93. 93 Type - Level It! WHEN ALL ELSE FAILS… trait Path[User, Role]: type Out = Query[(User, Role)] trait Path[User, Permission]: type Out = Query[(User, Role, Permission)] trait Path[User, Venue]: type Out = Query[(User, Role, Permission, Venue)] TypelevelUsecase.scala
  • 94. 94 inline given Path[User, Role]: type Out = Query[(User, Role)] inline def get: Query[(User, Role)] = for { u <- query[User] ur <- query[UserToRole].join(ur => ur.userId == s.id) r <- query[Role].join(r => r.id == ur.roleId) } yield (u, r) inline given Path[User, Permission]: type Out = Query[(User, Role, Permission)] inline def get: Query[(User, Role, Permission)] = for { u <- query[User] ur <- query[UserToRole].join(ur => ur.userId == s.id) r <- query[Role].join(r => r.id == ur.roleId) rp <- query[RoleToPermission].join(rp => rp.roleId == r.id) p <- query[Permission].join(p => p.id == rp.roleId) } yield (u, r, p) inline def path[F, T](using inline path: Path[F, T]): path.Out = path.get TypelevelUsecase.scala
  • 95. 95 Type - Level It! WHEN ALL ELSE FAILS… path[User,Role]: Query[(User, Role)] path[User,Permission]: Query[(User, Role, Permission)] path[User, Venue]: Query[(User, Role, Permission, Venue)] TypelevelUsecase.scala NOTE: This Slide was skipped in the talk to save time.
  • 96. 96 inline def q: Quoted[Query[(User, Role)]] = quote { path[User,Role] } inline def q: Quoted[Query[(User, Role, Permission)]] = quote { path[User,Permission] } inline def q: Quoted[Query[(User, Role, Permission, Venue)]] = quote { path[User,Venue] } Type - Level It! WHEN ALL ELSE FAILS… TypelevelUsecase.scala
  • 97. 97 inline def q: Quoted[Query[(User, Role)]] = quote { path[User,Role] } inline def q: Quoted[Query[(User, Role, Permission)]] = quote { path[User,Permission] } inline def q: Quoted[Query[(User, Role, Permission, Venue)]] = quote { path[User,Venue] } TypelevelUsecase.scala val userAndRoleAndPermissionAndVenue = quote { for { u <- query[User] ur <- query[UserToRole].join(so => so.userId == u.id) r <- query[Role].join(r => r.id == ur.roleId) rp <- query[RoleToPermission].join(rp => rp.roleId == r.id) p <- query[Permission].join(p => p.id == rp.roleId) v <- query[Venue].join(v => v.id == p.venueId) } yield (u, r, p, v) } val userAndRoleAndPermission = quote { for { u <- query[User] ur <- query[UserToRole].join(so => so.userId == u.id) r <- query[Role].join(r => r.id == ur.roleId) rp <- query[RoleToPermission].join(rp => rp.roleId == r.id) p <- query[Permission].join(p => p.id == rp.roleId) } yield (u, r, p) } val userAndRole = quote { for { u <- query[User] ur <- query[UserToRole].join(so => so.userId == u.id) r <- query[Role].join(r => r.id == ur.roleId) } yield (u, r, p) }
  • 98. inline def q = quote { path[User,Venue] .filter(t => t._1.name == "Joe" && t._2.name == "Drinker" && t._3.name == "Drink" && t._4.name == "Divebar") } 98inline def q: Quoted[Query[(User, Role)]] = quote { path[User,Role] .filter(t => t._1.name == "Joe" && t._2.name == "Drinker") } inline def q: Quoted[Query[(User, Role, Permission)]] = quote { path[User,Permission] .filter(t => t._1.name == "Joe" && t._2.name == "Drinker" && t._3.name == "Drink") } TypelevelUsecase.scala NOTE: This Slide was skipped in the talk to save time.
  • 99. inline def q: Quoted[Query[(User, Role, Permission, Venue)]] = quote { path[User,Venue] .filter(t => t._1.name == "Joe" && t._2.name == "Drinker" && t._3.name == "Drink" && t._4.name == "Divebar") } 99 SELECT u.id, u.name, r.id, r.name, p.id, p.name, p.venueId, v.id, v.name FROM User u INNER JOIN UserToRole so ON so.userId = u.id INNER JOIN Role r ON r.id = so.roleId INNER JOIN RoleToPermission rp ON rp.roleId = r.id INNER JOIN Permission p ON p.id = rp.roleId INNER JOIN Venue v ON v.id = p.venueId WHERE u.name = 'Joe' AND r.name = 'Drinker' AND p.name = 'Drink' AND v.name = 'Divebar' TypelevelUsecase.scala NOTE: This Slide was skipped in the talk to save time.
  • 100. inline def q = quote { path[User,Venue].filter { case (u, r, p, v) => u.name == "Joe" && r.name == "Drinker" && p.name == "Drink" && v.name == "Divebar" } } 100 SELECT u.id, u.name, r.id, r.name, p.id, p.name, p.venueId, v.id, v.name FROM User u INNER JOIN UserToRole so ON so.userId = u.id INNER JOIN Role r ON r.id = so.roleId INNER JOIN RoleToPermission rp ON rp.roleId = r.id INNER JOIN Permission p ON p.id = rp.roleId INNER JOIN Venue v ON v.id = p.venueId WHERE u.name = 'Joe' AND r.name = 'Drinker' AND p.name = 'Drink' AND v.name = 'Divebar' TypelevelUsecase.scala NOTE: This Slide was skipped in the talk to save time.
  • 101. inline def q = quote { path[User,Venue].filter((u, r, p, v) => u.name == "Joe" && r.name == "Drinker" && p.name == "Drink" && v.name == "Divebar" ) } 101 SELECT u.id, u.name, r.id, r.name, p.id, p.name, p.venueId, v.id, v.name FROM User u INNER JOIN UserToRole so ON so.userId = u.id INNER JOIN Role r ON r.id = so.roleId INNER JOIN RoleToPermission rp ON rp.roleId = r.id INNER JOIN Permission p ON p.id = rp.roleId INNER JOIN Venue v ON v.id = p.venueId WHERE u.name = 'Joe' AND r.name = 'Drinker' AND p.name = 'Drink' AND v.name = 'Divebar' TypelevelUsecase.scala NOTE: This Slide was skipped in the talk to save time.
  • 102. inline def q = quote { path[User,Venue].filter((u, r, p, v) => u.name == "Joe" && r.name == "Drinker" && p.name == "Drink" && v.name == "Divebar" ) } run(q) 102 sbt:dotty-simple> compile SELECT u.id, u.name, r.id, r.name, p.id, p.name, p.venueId, v.id, v.name FROM User u INNER JOIN UserToRole so ON so.userId = u.id INNER JOIN Role r ON r.id = so.roleId INNER JOIN RoleToPermission rp ON rp.roleId = r.id INNER JOIN Permission p ON p.id = rp.roleId INNER JOIN Venue v ON v.id = p.venueId WHERE u.name = 'Joe' AND r.name = 'Drinker' AND p.name = 'Drink' AND v.name = 'Divebar' [success] Total time: 7 s, completed Nov 23, 2020 12:16:08 AM TypelevelUsecase.scala NOTE: This Slide was skipped in the talk to save time.
  • 103. 103inline def q = quote { path[User,Role] .filter((u, r) => u.name == "Joe" && r.name == "Drinker") } inline def q = quote { path[User,Permission] .filter((u, r, p) => u.name == "Joe" && r.name == "Drinker" && p.name == "Drink") } inline def q = quote { path[User,Venue] .filter((u, r, p, v) => u.name == "Joe" && r.name == "Drinker" && p.name == "Drink" && v.name == "Divebar") } SELECT u.id, u.name, r.id, r.name, p.id, p.name, p.venueId, v.id, v.name FROM User u INNER JOIN UserToRole so ON so.userId = u.id INNER JOIN Role r ON r.id = so.roleId INNER JOIN RoleToPermission rp ON rp.roleId = r.id INNER JOIN Permission p ON p.id = rp.roleId INNER JOIN Venue v ON v.id = p.venueId WHERE u.name = 'Joe' AND r.name = 'Drinker' AND p.name = 'Drink' AND v.name = 'Divebar' SELECT u.id, u.name, r.id, r.name, p.id, p.name, p.venueId FROM User u INNER JOIN UserToRole so ON so.userId = u.id INNER JOIN Role r ON r.id = so.roleId INNER JOIN RoleToPermission rp ON rp.roleId = r.id INNER JOIN Permission p ON p.id = rp.roleId WHERE u.name = 'Joe' AND r.name = 'Drinker' AND p.name = 'Drink' SELECT u.id, u.name, r.id, r.name FROM User u INNER JOIN UserToRole so ON so.userId = u.id INNER JOIN Role r ON r.id = so.roleId WHERE u.name = 'Joe' AND r.name = 'Drinker' TypelevelUsecase.scala
  • 104. 104 How Far… Is just that little bit over there?
  • 105. 105 Conclusions In a good way! It will reveal many new applications in the years to come. Inline is the new Implicit Many new capabilities we've only dreamed about up to now! Dotty Enables Quill to be Awesome!