--
Daniel C. Sobral
I travel to the future all the time.
That's right. The fundamental discovery we made was that reify is
type-safe quasi-quote, so it's not clear one would still have a need
the latter (maybe for pattern matching). So we decided to drop quasi
quotes from the proposal.
Cheers
-- Martin
Sorry if I got something wrong (so please ignore it if I am to weak minded to
get the macro thing right) but I dislike the following part:
"However, macro selection always depends on the static type of the receiver
object, not the dynamic type"
The example given:
class C { def m() = 1 }
class D extends C { override def m() = macro mI }
val d = new D; d.m()
val c = d; c.m()
with d.m() calling the macro implementation and c.m() calling C.m() is [in my
eyes] a slap in the face of the principle of least surprise (and to be frank -
I fail to understand why this should be the case).
This assignment could happen everywhere in the source code (and not side by
side like in this example) and I would be very surprised to see this
behaviour. How would you (as a human) reason about this?
I hope I misread or misunderstood something entirely as the example and
behaviour explained above seems... not desireable.
Can you restore my faith?
astonished greetings
Bernd
But you read it right.
>
> "However, macro selection always depends on the static type of the receiver
> object, not the dynamic type"
>
> The example given:
>
> class C { def m() = 1 }
> class D extends C { override def m() = macro mI }
>
> val d = new D; d.m()
> val c = d; c.m()
that should be "val c: C = d".
>
> with d.m() calling the macro implementation and c.m() calling C.m() is [in my
> eyes] a slap in the face of the principle of least surprise (and to be frank -
> I fail to understand why this should be the case).
Consider this simple function:
def f(c: C) = c.m()
Now, a macro is something that changes the bytecode. The bytecode for
calling method m on C is an invokevirtual or something similar. The
bytecode for calling m on D is something completely different -- in
fact, it might not even generate bytecode at all (see the assert macro
for such an example). How can the compile possibly contemplate this
when compiling f? Hell, it might not even know anything about D at
this point -- that might get compiled elsewhere!
So, while it is obvious in "val c: C = d; c.m()" that c actually
contains a D, that is an exception case, and there's no practical way
of specifying this exception, and much space for confusion.
By the way, this is the behavior of collection methods as well -- the
static type defines what happens, not the dynamic type. Some people do
get surprised by it, so some people will get surprised by this. It
can't be helped, though.
On 9 Mrz., 22:50, Daniel Sobral <dcsob...@gmail.com> wrote:
> On Fri, Mar 9, 2012 at 17:28, Bernd Johannes <bjoha...@bacon.de> wrote:
>
> > Sorry if I got something wrong (so please ignore it if I am to weak minded to
> > get the macro thing right) but I dislike the following part:
>
> But you read it right.
>
>
>
> > "However, macro selection always depends on the static type of the receiver
> > object, not the dynamic type"
>
> > The example given:
>
> > class C { def m() = 1 }
> > class D extends C { override def m() = macro mI }
>
> > val d = new D; d.m()
> > val c = d; c.m()
>
> that should be "val c: C = d".
>
That's a game changer [faith restored]. I took the example literal and
couldn't follow why c and d should behave differently.
When I explicitly fix the type of c: C it is more obvious and
understandable (but might also cause some headache).
I am thinking about something like this:
def do_m(p: C) = p.m()
do_m(d)
It's not so obvious - may be there is a way to help to catch those
potential surprises.
Or think about:
d match { case x: C => x.m() }
I would like to see optional compiler warnings if a macro override
containing type instance is coerced into its super type.
Hi Bernd,
Am Freitag, 9. März 2012 23:49:50 UTC+1 schrieb Bernd:
On 9 Mrz., 22:50, Daniel Sobral <dcsob...@gmail.com> wrote:
> On Fri, Mar 9, 2012 at 17:28, Bernd Johannes <bjoha...@bacon.de> wrote:
>
> > Sorry if I got something wrong (so please ignore it if I am to weak minded to
> > get the macro thing right) but I dislike the following part:
>
> But you read it right.
>
>
>
> > "However, macro selection always depends on the static type of the receiver
> > object, not the dynamic type"
>
> > The example given:
>
> > class C { def m() = 1 }
> > class D extends C { override def m() = macro mI }
>
> > val d = new D; d.m()
> > val c = d; c.m()
>
> that should be "val c: C = d".
>
That's a game changer [faith restored]. I took the example literal and
couldn't follow why c and d should behave differently.
When I explicitly fix the type of c: C it is more obvious and
understandable (but might also cause some headache).
I am thinking about something like this:
def do_m(p: C) = p.m()
This is an interesting one: at this call site, p.m() is a virtual method call. So, all subclasses of C must implement this, otherwise NoSuchMethodError ensues. If D#m is a macro, how is the “non-specialized” method generated? I.e. in the SIP’s second example, what byte-code would I find in Queryable.class?
I guess the same reason why people spell class clazz, type tpe and so on: reserved keywords.
But yes ... I'm trying to think of a better name for it in general, too, but no success so far. :-)
- Why are macro implementations decoupled from macro defs?
Also, from the SIP:
"Relatively less important for us was the convenience of writing
macros, precisely because we anticipate that macros should be used
only in relatively rare cases, and that they should be reserved to
expert programmers who are familiar with language theory and compiler
concepts"
-jason
The main reason for splitting macro defs and macro impls is transparency. Previous approach with macro defs and macro impls merged had two deficiencies: 1) awkward interaction with lexical scope, 2) impossibility to specify exact mapping from a macro def to a macro impl.
Speaking of the macro def syntax, both of your proposals have been discussed internally.The first one hijacks the "equals" operator, which is also not ideal.