macro troubles, trying to produce multiple expressions (class def + block)

85 views
Skip to first unread message

Rob Nikander

unread,
May 13, 2013, 4:12:05 PM5/13/13
to scala-user
Hi,

I want a macro used like `query("select * from foo")` to generate code
like:

case class Row( /* fields from DB */)
val buf = new ListBuffer[Row]
val rs = conn.prepareStatement(sql).executeQuery()
while (rs.next) {
buf += Row(rs.getString(1), rs.getInt(2), ... fields from
DB ... ) // ***
}
lb.toList

But from what I know so far, I must put that all in a single block
expression, and that is generating type errors that I don't
understand. The compilation failed with:

MacroTests.scala:25: error: type mismatch;
found : Row
required: Row.type

I can deduce that this comes from the line marked with *** above. So
it didn't like my ListBuffer[Row] declaration. My tree looks like:

Apply(
Select(
New(
AppliedTypeTree(
Ident(listBufferSym),
List(Ident("Row"))
// List(Ident(typeOf[Any].typeSymbol)) -- this
get's it to run, but without desired type
)
),
nme.CONSTRUCTOR
),
List()
)

To understand why it failed I tried to reify this:

reify({ case class R(id: Int); val lb = new ListBuffer[R](); lb
+= R(1) })

In response, It printed a short story...

<console>:12: error: Cannot materialize {
[ lots of internally generated scala code ]
} because:
scala.reflect.macros.TypecheckException: type mismatch;
found : $u.TypeTag[(some other)lb.type(in value res4)
forSome { type (some other)lb.type(in value res4) <:
scala.collection.mutable.ListBuffer[(some other)R(in value res4)] with
Singleton; type (some other)R(in value res4) <: Product with
Serializable{val id: Int; def copy(id: Int): (some other)R(in value
res4); def copy$default$1: Int} }]
required: $u.WeakTypeTag[lb.type(in value res4) forSome
{ type lb.type(in value res4) <:
scala.collection.mutable.ListBuffer[R(in value res4)] with Singleton;
type R(in value res4) <: Product with Serializable{val id: Int; def
copy(id: Int): R(in value res4); def copy$default$1: Int} }]

I don't understand that. I haven't learned about 'forSome' yet.

Any suggestions? Is there a way for the macro to introduce multiple
expressions without wrapping them in a block? (And let me know if
there a more appropriate mailing list for this.)

thank you,
Rob

Eugene Burmako

unread,
May 13, 2013, 4:16:24 PM5/13/13
to Rob Nikander, scala-user
Try Ident(newTypeName("Row"))



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



Eugene Burmako

unread,
May 13, 2013, 4:18:41 PM5/13/13
to Rob Nikander, scala-user
Answering your other questions:
1) Macros can only expand into multiple expressions if those are wrapped in a block.
2) Please submit the problem with reify to JIRA. This failure indicates a compiler bug.


On 13 May 2013 22:12, Rob Nikander <rob.ni...@gmail.com> wrote:

Eugene Burmako

unread,
May 13, 2013, 4:19:24 PM5/13/13
to Rob Nikander, scala-user
Oh btw this works:

scala> reify({ case class R(id: Int); val lb = new ListBuffer[R](); lb += R(1); () })
res4: reflect.runtime.universe.Expr[Unit] =
Expr[Unit]({
  case class R extends Product with Serializable {
    <caseaccessor> <paramaccessor> val id: Int = _;
    def <init>(id: Int) = {
      super.<init>();
      ()
    }
  };

  val lb = new ListBuffer[R]();
  lb.$plus$eq(R.apply(1));
  ()
})


On 13 May 2013 22:12, Rob Nikander <rob.ni...@gmail.com> wrote:

Rob Nikander

unread,
May 13, 2013, 4:33:31 PM5/13/13
to scala-user

On May 13, 4:16 pm, Eugene Burmako <eugene.burm...@epfl.ch> wrote:
> Try Ident(newTypeName("Row"))
>

That works. Thanks!

Rob Nikander

unread,
May 13, 2013, 4:43:55 PM5/13/13
to scala-user


On May 13, 4:18 pm, Eugene Burmako <eugene.burm...@epfl.ch> wrote:
> 2) Please submit the problem with reify to JIRA. This failure indicates a
> compiler bug.


Done.

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




Rob Nikander

unread,
May 13, 2013, 5:18:11 PM5/13/13
to scala-user


On May 13, 4:18 pm, Eugene Burmako <eugene.burm...@epfl.ch> wrote:
> 1) Macros can only expand into multiple expressions if those are wrapped in
> a block.

Is something like this possible?

class/object X {
val sql = "select * from foo"
m(sql)
}

where:
1. macro m expands to a class definition
2. m's implementation can see the constant string value of sql,
"select * from foo".

Rob

Eugene Burmako

unread,
May 13, 2013, 5:21:33 PM5/13/13
to Rob Nikander, scala-user
1) m can expand to a class definition, but it won't be visible from outside of the expansion.
2) unfortunately, in general case this is either very hard or impossible.



Rob

Rob Nikander

unread,
May 13, 2013, 5:51:59 PM5/13/13
to scala-user
Okay, it sounds like I won't be able to create what I was imagining --
a DB query macro that prepared the JDBC statement at compile time and
generated a row class with typesafe fields. I got it working with
tuples, but to access the columns with named fields it sounds like I
must build a separate tool that generates .scala files.

Thanks again for the helpful responses.

Rob

On May 13, 5:21 pm, Eugene Burmako <eugene.burm...@epfl.ch> wrote:
> 1) m can expand to a class definition, but it won't be visible from outside
> of the expansion.
> 2) unfortunately, in general case this is either very hard or impossible.
>

Eugene Burmako

unread,
May 13, 2013, 5:54:26 PM5/13/13
to Rob Nikander, scala-user, Christopher Vogt, Stefan Zeiger
Do you have to you use Scala 2.10.x, or an experimental version of scalac will do?

Rob Nikander

unread,
May 13, 2013, 6:01:01 PM5/13/13
to scala-user
I can use experimental.

Rob

On May 13, 5:54 pm, Eugene Burmako <eugene.burm...@epfl.ch> wrote:
> Do you have to you use Scala 2.10.x, or an experimental version of scalac
> will do?
>

Eugene Burmako

unread,
May 13, 2013, 6:03:02 PM5/13/13
to Rob Nikander, scala-user

Simon Ochsenreither

unread,
May 13, 2013, 7:05:02 PM5/13/13
to scala...@googlegroups.com, Rob Nikander
https://github.com/jonifreeman/sqltyped might be of interest, too.

Rob Nikander

unread,
May 14, 2013, 3:41:06 PM5/14/13
to Eugene Burmako, scala-user
Will I be able to use the Eclipse Scala IDE with this macro paradise compiler? 

Eugene Burmako

unread,
May 14, 2013, 3:46:09 PM5/14/13
to Rob Nikander, Eugene Burmako, scala-user
Good question. I haven't done anything special to enable that, so if it breaks I wouldn't be surprised. Are you having problems?

Rob Nikander

unread,
May 15, 2013, 9:21:55 AM5/15/13
to Eugene Burmako, scala-user
On Tue, May 14, 2013 at 3:46 PM, Eugene Burmako <xen...@gmail.com> wrote:
Good question. I haven't done anything special to enable that, so if it breaks I wouldn't be surprised. Are you having problems?

It appears I'll need to build a custom Scala IDE, because it comes with a baked-in compiler and it can't switch to others. This may stop me on this path for now.

Adriaan Moors

unread,
May 21, 2013, 5:11:23 PM5/21/13
to scala...@googlegroups.com, Eugene Burmako
You should be able to swap out the compiler jar when using an IDE version that's "close enough"
Reply all
Reply to author
Forward
0 new messages