11# Symmetric Meta Programming
22
3- Symmetric meta programming is a new framework for staging and certain
3+ Symmetric meta programming is a new framework for staging and for some
44forms of macros. It is is expressed as strongly and statically typed
55code using two fundamental operations: quotations and splicing. A
66novel aspect of the approach is that these two operations are
@@ -116,27 +116,30 @@ PCP. This is explained further in a later section.
116116
117117## Details
118118
119- ### ` Expr ` as an Applicative
119+ ### From ` Expr ` s to Functions and Back
120120
121- We postulate an implicit "Apply " decorator that turns a tree
121+ The ` Expr ` companion object contains an "AsFunction " decorator that turns a tree
122122describing a function into a function mapping trees to trees.
123123
124- implicit class AsApplicative[T, U](f: Expr[T => U]) extends AnyVal {
125- def apply(x: Expr[T]): Expr[U] = ???
124+ object Expr {
125+ ...
126+ implicit class AsFunction[T, U](f: Expr[T => U]) extends AnyVal {
127+ def apply(x: Expr[T]): Expr[U] = ???
128+ }
126129 }
127130
128- This decorator turns ` Expr ` into an applicative functor, where ` Expr ` s
131+ This decorator gives ` Expr ` the ` apply ` operation of an applicative functor, where ` Expr ` s
129132over function types can be applied to ` Expr ` arguments. The definition
130- of ` AsApplicative (f).apply(x)` is assumed to be functionally the same as
133+ of ` AsFunction (f).apply(x)` is assumed to be functionally the same as
131134` ’((~f)(~x)) ` , however it should optimize this call by returning the
132135result of beta-reducing ` f(x) ` if ` f ` is a known lambda expression
133136
134- The ` AsApplicative ` decorator distributes applications of ` Expr ` over function
137+ The ` AsFunction ` decorator distributes applications of ` Expr ` over function
135138arrows:
136139
137- AsApplicative (_).apply: Expr[S => T] => (Expr[S] => Expr[T])
140+ AsFunction (_).apply: Expr[S => T] => (Expr[S] => Expr[T])
138141
139- The dual of expansion , let’s call it ` reflect ` , can be defined as follows:
142+ Its dual, let’s call it ` reflect ` , can be defined as follows:
140143
141144 def reflect[T, U](f: Expr[T] => Expr[U]): Expr[T => U] = ’{
142145 (x: T) => ~f(’(x))
@@ -253,7 +256,7 @@ Here’s an application of `map` and how it rewrites to optimized code:
253256
254257### Relationship with Inline and Macros
255258
256- Seen by itself, principled meta-programming looks more like a
259+ Seen by itself, symmetric meta-programming looks more like a
257260framework for staging than one for compile-time meta programming with
258261macros. But combined with Dotty’s ` inline ` it can be turned into a
259262compile-time system. The idea is that macro elaboration can be
@@ -307,7 +310,7 @@ If `program` is treated as a quoted expression, the call to
307310` Macro.assertImpl ` becomes phase correct even if macro library and
308311program are conceptualized as local definitions.
309312
310- But what about the call from ` assert ` to `assertImpl? Here, we need a
313+ But what about the call from ` assert ` to ` assertImpl ` ? Here, we need a
311314tweak of the typing rules. An inline function such as ` assert ` that
312315contains a splice operation outside an enclosing quote is called a
313316_ macro_ . Macros are supposed to be expanded in a subsequent phase,
@@ -318,7 +321,7 @@ the call from `assert` to `assertImpl` phase-correct, even if we
318321assume that both definitions are local.
319322
320323The second role of ` inline ` in Dotty is to mark a ` val ` that is
321- constant or a parameter that will be constant when instantiated. This
324+ either a constant or is a parameter that will be a constant when instantiated. This
322325aspect is also important for macro expansion. To illustrate this,
323326consider an implementation of the ` power ` function that makes use of a
324327statically known exponent:
@@ -331,7 +334,7 @@ statically known exponent:
331334 else if (n % 2 == 0) ’{ { val y = ~x * ~x; ~powerCode(n / 2, ’(y)) } }
332335 else ’{ ~x * ~powerCode(n - 1, x) }
333336
334- The usage of ` n ` as an argument in ` ~powerCode(n, ’(x)) ` is not
337+ The reference to ` n ` as an argument in ` ~powerCode(n, ’(x)) ` is not
335338phase-consistent, since ` n ` appears in a splice without an enclosing
336339quote. Normally that would be a problem because it means that we need
337340the _ value_ of ` n ` at compile time, which is not available for general
@@ -435,10 +438,10 @@ Running `compile(letExp, Map())` would yield the following Scala code:
435438
436439 ’{ val y = 3; (2 + y) + 4 }
437440
438- The body the first clause ( ` case Num(n) => n ` ) looks suspicious. ` n `
441+ The body of the first clause, ` case Num(n) => n ` , looks suspicious. ` n `
439442is declared as an ` Int ` , yet the result of ` compile ` is declared to be
440- ` Expr[Int] ` . Shouldn’t `n be quoted? The answer is that this would not
441- work since replacing ` n by ` ’n` in the clause would not be phase
443+ ` Expr[Int] ` . Shouldn’t ` n ` be quoted? In fact this would not
444+ work since replacing ` n ` by ` ’n ` in the clause would not be phase
442445correct.
443446
444447What happens instead "under the hood" is an implicit conversion: ` n `
@@ -480,7 +483,7 @@ tree machinery:
480483 }
481484 }
482485
483- Since ` Liftable ` is a type class, instances can be conditional. For instance
486+ Since ` Liftable ` is a type class, its instances can be conditional. For example,
484487a ` List ` is liftable if its element type is:
485488
486489 implicit def ListIsLiftable[T: Liftable]: Liftable[List[T]] = new {
@@ -505,7 +508,7 @@ need a syntax change that introduces prefix operators as types.
505508 SimpleType ::= ...
506509 [‘-’ | ‘+’ | ‘~’ | ‘!’] StableId
507510
508- Analogously to the situation with expressions a prefix type operator
511+ Analogously to the situation with expressions, a prefix type operator
509512such as ` ~ e ` is treated as a shorthand for the type ` e.unary_~ ` .
510513
511514Quotes are supported by introducing new tokens ` ’( ` , ` ’{ ` , and ` ’[ `
@@ -521,7 +524,7 @@ Syntax changes are given relative to the [Dotty reference
521524grammar] ( ../internal/syntax.md ) .
522525
523526An alternative syntax would treat ` ’ ` as a separate operator. This
524- would be attractive since it enables quoting single identifies as
527+ would be attractive since it enables quoting single identifiers as
525528e.g. ` ’x ` instead of ` ’(x) ` . But it would clash with symbol
526529literals. So it could be done only if symbol literals were abolished.
527530
@@ -538,7 +541,7 @@ that method. With the restrictions on splices that are currently in
538541place that’s all that’s needed. We might allow more interpretation in
539542splices in the future, which would allow us to loosen the
540543restriction. Quotes in spliced, interpreted code are kept as they
541- are, after splices nested in the quotes are expanded recursively .
544+ are, after splices nested in the quotes are expanded.
542545
543546If the outermost scope is a quote, we need to generate code that
544547constructs the quoted tree at run-time. We implement this by
@@ -575,7 +578,7 @@ The syntax of terms, values, and types is given as follows:
575578
576579Typing rules are formulated using a stack of environments
577580` Es ` . Individual environments ` E ` consist as usual of variable
578- bindings ` x: T ` . Environments can be combined using one of two
581+ bindings ` x: T ` . Environments can be combined using the two
579582combinators ` ’ ` and ` ~ ` .
580583
581584 Environment E ::= () empty
@@ -608,7 +611,7 @@ We define a small step reduction relation `-->` with the following rules:
608611The first rule is standard call-by-value beta-reduction. The second
609612rule says that splice and quotes cancel each other out. The third rule
610613is a context rule; it says that reduction is allowed in the hole ` [ ] `
611- position of an evaluation contexts . Evaluation contexts ` e ` and
614+ position of an evaluation context . Evaluation contexts ` e ` and
612615splice evaluation context ` e_s ` are defined syntactically as follows:
613616
614617 Eval context e ::= [ ] | e t | v e | ’e_s[~e]
@@ -662,7 +665,7 @@ environments and terms.
662665
663666## Going Further
664667
665- The presented meta-programming framework is so far quite restrictive
668+ The meta-programming framework as presented and currently implemented is quite restrictive
666669in that it does not allow for the inspection of quoted expressions and
667670types. It’s possible to work around this by providing all necessary
668671information as normal, unquoted inline parameters. But we would gain
@@ -687,11 +690,11 @@ implementation of power otherwise.
687690This assumes a ` Constant ` extractor that maps tree nodes representing
688691constants to their values.
689692
690- Once we allow for inspection of code, the "AsApplicative " operation
693+ With the right extractors the "AsFunction " operation
691694that maps expressions over functions to functions over expressions can
692695be implemented in user code:
693696
694- implicit class AsApplicative [T, U](f: Expr[T => U]) extends AnyVal {
697+ implicit class AsFunction [T, U](f: Expr[T => U]) extends AnyVal {
695698 def apply(x: Expr[T]): Expr[U] =
696699 f match {
697700 case Lambda(g) => g(x)
@@ -705,7 +708,7 @@ This assumes an extractor
705708 }
706709
707710Once we allow inspection of code via extractors, it’s tempting to also
708- add constructors that construct typed trees directly without going
711+ add constructors that create typed trees directly without going
709712through quotes. Most likely, those constructors would work over ` Expr `
710713types which lack a known type argument. For instance, an ` Apply `
711714constructor could be typed as follows:
@@ -722,13 +725,14 @@ implemented as a primitive; it would check that the computed type
722725structure of ` Expr ` is a subtype of the type structure representing
723726` T ` .
724727
725- Before going down that route, we should carefully evaluate its
726- tradeoffs . Constructing trees that are only verified _ a posteriori_
728+ Before going down that route, we should evaluate in detail the tradeoffs it
729+ presents . Constructing trees that are only verified _ a posteriori_
727730to be type correct loses a lot of guidance for constructing the right
728731trees. So we should wait with this addition until we have more
729732use-cases that help us decide whether the loss in type-safety is worth
730- the gain in flexibility.
731-
733+ the gain in flexibility. In this context, it seems that deconstructing types is
734+ less error-prone than deconstructing tersm, so one might also
735+ envisage a solution that allows the former but not the latter.
732736
733737## Conclusion
734738
0 commit comments