Macros adding to the implicit scope

453 views
Skip to first unread message

Miles Sabin

unread,
May 8, 2012, 8:06:00 AM5/8/12
to scala-i...@googlegroups.com
Hi folks,

Is there, or is there ever likely to be, a mechanism allowing a macro
to add implicit definitions to the implicit scope of its calling
context?

For example, could we write a macro,

def publish[T](t : T) = macro publishImpl(t)

def publishImpl[T](c : Context)(t : c.Expr[T]) = ...

such that when called,

publish("foo")

it expands at the call site to,

implicit val <freshname> = "foo"

Cheers,


Miles

--
Miles Sabin
tel: +44 7813 944 528
gtalk: mi...@milessabin.com
skype: milessabin
g+: http://www.milessabin.com
http://twitter.com/milessabin
http://underscoreconsulting.com
http://www.chuusai.com

Eugene Burmako

unread,
May 8, 2012, 8:29:16 AM5/8/12
to scala-i...@googlegroups.com
Currently this won't work, because (as far as I can guess by interpreting the error) macro expansion happens too late to be included in scope.

Could you submit a bug, though? I'll get to it later.

Miles Sabin

unread,
May 8, 2012, 8:31:53 AM5/8/12
to scala-i...@googlegroups.com
On Tue, May 8, 2012 at 1:29 PM, Eugene Burmako <eugene....@epfl.ch> wrote:
> Currently this won't work, because (as far as I can guess by interpreting
> the error) macro expansion happens too late to be included in scope.
>
> Could you submit a bug, though? I'll get to it later.

Will do. Could you post the example you tried that gave the error? I
didn't see any obvious way of injecting any sort of name (implicit or
otherwise) into the calling scope, so I didn't even attempt to write
something that'd fail ;-)

Eugene Burmako

unread,
May 8, 2012, 8:55:17 AM5/8/12
to scala-i...@googlegroups.com
import scala.reflect.makro.Context

object Macros {
   def publish = macro publishImpl

   def publishImpl(c : Context) = c.reify{implicit val foo: String = "foo"}
}

import Macros._

object Test extends App {
  publish;
  println(implicitly[String])
}

Miles Sabin

unread,
May 8, 2012, 9:09:45 AM5/8/12
to scala-i...@googlegroups.com
On Tue, May 8, 2012 at 1:55 PM, Eugene Burmako <eugene....@epfl.ch> wrote:
> import scala.reflect.makro.Context
>
> object Macros {
>    def publish = macro publishImpl
>
>    def publishImpl(c : Context) = c.reify{implicit val foo: String = "foo"}
> }
>
> import Macros._
>
> object Test extends App {
>   publish;
>   println(implicitly[String])
> }

Oh, right ... but that's not an expression ... is there any chance at
all of that working?

Stefan Zeiger

unread,
May 8, 2012, 9:29:11 AM5/8/12
to scala-i...@googlegroups.com
On 2012-05-08 14:55, Eugene Burmako wrote:
> import scala.reflect.makro.Context
>
> object Macros {
> def publish = macro publishImpl
>
> def publishImpl(c : Context) = c.reify{implicit val foo: String =
> "foo"}
> }

I thought I'd try injecting the implicit into some inlined code block
that I could pass to the macro:

def publish2(f: => Unit) = macro publish2Impl

def publish2Impl(c : Context)(f: c.Expr[Function0[Unit]]) =
c.reify{implicit val foo: String = "foo"; f.eval}

But that gives me:

[error]
C:\Users\szeiger\code\macrostuff\src\main\scala\com\novocode\macrostuff\MacroStuff.scala:69:
macro implementation has wrong shape:
[error] required: (c: scala.reflect.makro.Context)(f: c.Expr[=> Unit]):
c.Expr[() => Unit]
[error] found : (c: scala.reflect.makro.Context)(f: c.Expr[() =>
Unit]): c.Expr[() => Unit]
[error] type mismatch for parameter f: c.Expr[=> Unit] does not conform
to c.Expr[() => Unit]
[error] def publish2(f: => Unit) = macro publish2Impl
[error] ^

AFAIK call-by-name parameters are converted to Function0 behind the
scenes but the compiler doesn't like that here. Is there any way to
actually write the type it expects?

-sz

Miles Sabin

unread,
May 8, 2012, 9:31:04 AM5/8/12
to scala-i...@googlegroups.com
On Tue, May 8, 2012 at 2:29 PM, Stefan Zeiger <sze...@novocode.com> wrote:
> I thought I'd try injecting the implicit into some inlined code block that I
> could pass to the macro:
>
>  def publish2(f: => Unit) = macro publish2Impl
>
>  def publish2Impl(c : Context)(f: c.Expr[Function0[Unit]]) =
> c.reify{implicit val foo: String = "foo"; f.eval}

That would also be very useful :-)

Stefan Zeiger

unread,
May 8, 2012, 9:34:41 AM5/8/12
to scala-i...@googlegroups.com
On 2012-05-08 15:31, Miles Sabin wrote:
> On Tue, May 8, 2012 at 2:29 PM, Stefan Zeiger<sze...@novocode.com> wrote:
>> I thought I'd try injecting the implicit into some inlined code block that I
>> could pass to the macro:
>>
>> def publish2(f: => Unit) = macro publish2Impl
>>
>> def publish2Impl(c : Context)(f: c.Expr[Function0[Unit]]) =
>> c.reify{implicit val foo: String = "foo"; f.eval}
> That would also be very useful :-)

Actually, I think it wouldn't (but I only realized that after posting).
There's not really a need to do call-by-name if you get the AST anyway.
What I posted above should work fine with a plain f: Unit parameter. But
it still runs into the same scoping issue as the original version.

-sz

Miles Sabin

unread,
May 8, 2012, 9:37:07 AM5/8/12
to scala-i...@googlegroups.com
On Tue, May 8, 2012 at 2:34 PM, Stefan Zeiger <sze...@novocode.com> wrote:
> Actually, I think it wouldn't (but I only realized that after posting).
> There's not really a need to do call-by-name if you get the AST anyway. What
> I posted above should work fine with a plain f: Unit parameter. But it still
> runs into the same scoping issue as the original version.

True, but injecting a value into the argument via an implicit has to
be easier for the macro writer than doing it via a tree rewrite, no?

Miles Sabin

unread,
May 8, 2012, 9:42:37 AM5/8/12
to scala-i...@googlegroups.com
On Tue, May 8, 2012 at 1:29 PM, Eugene Burmako <eugene....@epfl.ch> wrote:
> Currently this won't work, because (as far as I can guess by interpreting
> the error) macro expansion happens too late to be included in scope.
>
> Could you submit a bug, though? I'll get to it later.

Ticket here,

https://issues.scala-lang.org/browse/SI-5774

Stefan Zeiger

unread,
May 8, 2012, 9:48:54 AM5/8/12
to scala-i...@googlegroups.com
On 2012-05-08 15:37, Miles Sabin wrote:
> On Tue, May 8, 2012 at 2:34 PM, Stefan Zeiger<sze...@novocode.com> wrote:
>> Actually, I think it wouldn't (but I only realized that after posting).
>> There's not really a need to do call-by-name if you get the AST anyway. What
>> I posted above should work fine with a plain f: Unit parameter. But it still
>> runs into the same scoping issue as the original version.
> True, but injecting a value into the argument via an implicit has to
> be easier for the macro writer than doing it via a tree rewrite, no?

I think it's pretty much the only way to do it without untyped macros
(if the scoping worked as expected) but by-name parameters would only
create some unnecessary overhead. This feature would be ideal for
statically scoped session handling. Currently we have this in SLICK:

def withSession[T](f: Session => T): T = {
val s = createSession()
try { f(s) } finally s.close()
}

def withSession[T](f: => T): T = withSession { s: Session =>
Database.dyn.withValue(s)(f) }

Either pass around the session value (or make your your own implicit
value for it), or rely on a dynamically scoped thread-local session.
With macros, we could do:

def withSession[T](f: T) = macro withSession_impl

def withSession_impl[T : TypeTag](c : Context)(f: c.Expr[T]) = c.reify{
c.prefix.eval.withSession[T] { s =>
implicit val session = s
f.eval
}
}

OTOH, implicit values affect type-checking, so maybe that's a lost cause
unless we get untyped macros.

-sz

Miles Sabin

unread,
May 8, 2012, 9:53:32 AM5/8/12
to scala-i...@googlegroups.com
On Tue, May 8, 2012 at 2:48 PM, Stefan Zeiger <sze...@novocode.com> wrote:
> OTOH, implicit values affect type-checking, so maybe that's a lost cause
> unless we get untyped macros.

An implicit argument with a default value would typecheck ...

Stefan Zeiger

unread,
May 8, 2012, 10:03:27 AM5/8/12
to scala-i...@googlegroups.com
On 2012-05-08 15:53, Miles Sabin wrote:
> On Tue, May 8, 2012 at 2:48 PM, Stefan Zeiger<sze...@novocode.com> wrote:
>> OTOH, implicit values affect type-checking, so maybe that's a lost cause
>> unless we get untyped macros.
> An implicit argument with a default value would typecheck ...

But that's just as unsafe as the current approach with the
threadLocalSession. Nobody prevents you from accessing that value
outside the proper scope.

Oh, wait, if the fall-back implicit was a macro that caused compilation
to fail... That might actually work. You'd still have to decide where
you need to import it (like the threadLocalSession) and where you pass
your own value instead but you'd get static scoping and compile-time safety.

-sz

Stefan Zeiger

unread,
May 8, 2012, 10:30:20 AM5/8/12
to scala-i...@googlegroups.com
On 2012-05-08 16:03, Stefan Zeiger wrote:
> Oh, wait, if the fall-back implicit was a macro that caused
> compilation to fail... That might actually work. You'd still have to
> decide where you need to import it (like the threadLocalSession) and
> where you pass your own value instead but you'd get static scoping and
> compile-time safety.

Nah, doesn't work, either. An implicit def implemented by a macro is not
found during implicit search, and if I make it a regular method,
everything compiles but the resulting code still references the original
implicit and not the new one injected by the macro. It's all coming back
to the original scoping problem.

-sz

Eugene Burmako

unread,
May 8, 2012, 6:27:33 PM5/8/12
to scala-internals
This begs for a bug report :)

On May 8, 3:29 pm, Stefan Zeiger <szei...@novocode.com> wrote:
> On 2012-05-08 14:55, Eugene Burmako wrote:
>
> > import scala.reflect.makro.Context
>
> > object Macros {
> >    def publish = macro publishImpl
>
> >    def publishImpl(c : Context) = c.reify{implicit val foo: String =
> > "foo"}
> > }
>
> I thought I'd try injecting the implicit into some inlined code block
> that I could pass to the macro:
>
>    def publish2(f: => Unit) = macro publish2Impl
>
>    def publish2Impl(c : Context)(f: c.Expr[Function0[Unit]]) =
> c.reify{implicit val foo: String = "foo"; f.eval}
>
> But that gives me:
>
> [error]
> C:\Users\szeiger\code\macrostuff\src\main\scala\com\novocode\macrostuff\Mac­roStuff.scala:69:

Stefan Zeiger

unread,
May 9, 2012, 6:17:51 AM5/9/12
to scala-i...@googlegroups.com
On 2012-05-09 0:27, Eugene Burmako wrote:
> This begs for a bug report :)
Done: https://issues.scala-lang.org/browse/SI-5778

-sz

Stefan Zeiger

unread,
May 9, 2012, 6:20:59 AM5/9/12
to scala-i...@googlegroups.com
One final addition to this monologue: Implicits implemented by macros
*are* found. The problem is that they are expanded during implicit
search (even if they specify a return type so it is not needed for
typing them) and throwing an Exception in the macro causes implicit
search to fail silently.

-sz

Eugene Burmako

unread,
May 9, 2012, 6:24:01 AM5/9/12
to scala-i...@googlegroups.com
Yeah, this is something I need to review once I have time for implicit macros.

As to swallowing macro exceptions, this is what I'm addressing right now.

Kyrylo Stokoz

unread,
Sep 20, 2015, 4:27:33 PM9/20/15
to scala-internals
Hi,

I know this is quite old thread, but what is current state of the issue?

 https://issues.scala-lang.org/browse/SI-5778  is fixed, but it looks like implicits added by macro cannot be resolved.

Here is an example:

object Example extends App {

Macros.run {
todo
}

def todo(implicit i: Int): Unit =
println(i)

}

object Macros {

def run(f: => Unit): Unit = macro runImpl

def runImpl(c : Context)(f: c.Tree) = {
import c.universe._
q"""{
implicit val i: Int = 3
$f
}"""
}
}

is not compiling with following error:
Error:(7, 9) could not find implicit value for parameter i: Int
        todo
        ^
 

Eugene Burmako

unread,
Sep 22, 2015, 8:18:48 AM9/22/15
to <scala-internals@googlegroups.com>
Nothing has changed since the previous discussion.

Def macros can't introduce definitions visible outside their expansions. It's possible to do what you want with macro annotations, but they aren't shipped in the standard distribution and require an external compiler plugin called macro paradise.

--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Kyrylo Stokoz

unread,
Sep 22, 2015, 12:23:30 PM9/22/15
to scala-internals
Can you give me some hints how macro annotation can help me here?

Which part of code would i annotate to get things working?

Thanks in advance,
Kyrylo

Eugene Burmako

unread,
Sep 25, 2015, 11:06:19 AM9/25/15
to <scala-internals@googlegroups.com>
Instead of:

object Example extends App {
    Macros.run { todo }
    def todo(implicit i: Int): Unit = println(i)
}

It would be something like:

object Example extends App {
    @something implicit val x = ???
    def todo(implicit i: Int): Unit = println(i)
}

There could be other possibilities. Unfortunately, none of them is particularly elegant syntactically.

Reply all
Reply to author
Forward
0 new messages