Mateusz Kubuszok
Note
|
Preparations: - open Chimney project (compiling!, 0.8.2) - open Pipez (compiling!, 0.5.1) - open terminal - 1 tab for examples, 1 for Chimney, 1 for Pipez - arrange them to easily jump from one to another - put speakers notes on laptop |
-
breaking things in Scala for 8+ years
-
breaking things for money for 10 years
-
breaking things for fun for 18(?) years
-
a little bit of open source - including co-authoring Chimney for 6 years now
-
blog at Kubuszok.com
-
niche Things you need to know about JVM (that matter in Scala) ebook
Note
|
Every presentation should start with some excuse, why you even are here. |
-
What is a macro method?
-
Expressions and types
-
Symbols
-
Examples showing similarities and differences when solving the same small problems
-
I will focus on examples that I saw while maintaining my libraries
-
I will NOT focus on macro annotations
Note
|
We will demonstrate these concepts using Scala CLI scripts. |
We can intuitively think that macro method is a code generator pretending to be some object’s method.
Note
|
We’ll explain what we mean by that in the next few examples. We’ll show how macros differ in behavior from normal method. |
-
Calling the impl method is the only thing we are allowed to do.
-
Expr[A]
is the AST of code that represents the value of typeA
. -
Why Scala 2 call it blackbox will be explained later.
-
Scala 3 has "global" expressions while Scala 2 use path-dependent types for them.
Note
|
We’re start with the simplest code that returns a constant value ( I can mention that expression is basically anything that computes a value. |
-
Impl doesn’t have to be in the same definition as unquoting - it doesn’t even have to be in the same package!
-
Impl has to be defined in a preceeding compilation unit to call site (macro referring to it can be in the same as call site).
Note
|
Then we’re going to look at the example above but with classes ( This is the first trope that we shouldn’t treat macros as methods - they don’t even have to be define in the same place as what unquotes them. |
Note
|
Now let’s call some method in the same object as the macro ( We are ancouraged to use full qualified name in Scala 2 as it doesn’t understand context in quasiquotes. If we used private method, compilation would fail since external code cannot access private methods ( This is the first example showing why I consider macro method to be a codegen rather than a method. |
Note
|
At first let’s print some parameters to see how printing works ( Then let’s try to print Scala 2 require the same names and positions of parametrs in macro and in called impl definition. Scala 2 require path-dependent type for WeakTypeTag as well, Scala 3 does not require it (like with Expr). Typed representations exists to be passed around (as params/returns/implicits), untyped can be actually worked with. Scala 2 contains a special value for what was before macro, and Scala 3 requires us to pass it explicitly. |
Scala 2 |
Scala 3 |
|
|
|
|
|
|
|
|
|
|
|
no counterpart |
Note
|
Scala 2 require the same names and positions of parametrs in macro and in called impl definition. Scala 2 require path-dependent type for WeakTypeTag as well, Scala 3 does not require it (like with Expr). Typed representations exists to be passed around (as params/returns/implicits), untyped can be actually worked with. Scala 2 contains a special value for what was before macro, and Scala 3 requires us to pass it explicitly. |
Scala 2 |
Scala 3 |
|
|
|
|
|
|
|
|
Note
|
|
Scala 2 |
Scala 3 |
|
|
no counterpart |
|
|
|
|
|
no counterpart |
|
|
|
Note
|
At first let’s print some parameters to see how printing works ( Then let’s try to print Scala 2 always carries around what was "before" dot macro method name, Scala 3 requires explicit passing of this. Scala 2 require path-dependent type for WeakTypeTag as well, Scala 3 does not require it (like with Expr). Scala 2 require the same names and positions of parametrs in macro and in called impl definition. Scala 2 contains a special value for what was before macro, and Scala 3 requires us to pass it explicitly. |
Scala 2 |
Scala 3 |
|
|
|
|
|
|
|
|
|
|
Note
|
Show example of Explain why |
Symbol - a reference to definition (type, class, val, var, method, parameter, binding…).
Scala 2 |
Scala 3 |
|
|
|
|
|
only 1 kind of |
|
|
|
|
|
|
|
|
Note
|
Let’s try to see what information we can obtain from the type (
I can explain that Symbol is basically anything which can have a name or handle to be referred to. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Note
|
Mention SIP-47 Clause Interleaving. |
Example:
-
take a type of a
case class
/sealed trait
-
try to create
List
with a value of this type -
for
case class
create a value if all params has default value -
for
sealed
, create all children that can be created (case object
s lubcase class
es like above)
Note
|
Show example ( Show that while it looks ok, it doesn’t support all cases. |
Note
|
Examples:
Also mention that:
1. default values in case class have different names ( |
Scala 2 |
Scala 3 |
||
|
|
|
|
|
|
|
|
"macro bundle" |
no counterpart |
Note
|
Show whitebox macros and transparent inline defs. Show macro bundles on Scala 2, and what Scala 3 has. |
-
basic concepts - typed and untyped expressions and types, AST, Symbols - are the same
-
Scala 2 APIs have more utilities, Scala 3 had more consistent utilities
-
both implementations have enough features to build upon them
-
both implementations have rather basic documentation
-
examples and slides available on my GitHub (GitHub.com/MateuszKubuszok)