macros

51 views
Skip to first unread message

marcos rebelo

unread,
Apr 22, 2017, 2:25:10 PM4/22/17
to scala-user
Hy all,

I'm willing to transform my case classes to a Squema at compile time.

Basically I need to get a Tree.

The case classes that I want to generate

====================================
package common

import scala.language.implicitConversions
import scala.language.higherKinds
import scala.language.experimental.macros

sealed trait SQLSchema[T]
case class RawSQLSchema[T](typ: String) extends SQLSchema[T]
case class RecordSQLSchema[T](subfields: List[(String, SQLSchema[_])]) extends SQLSchema[T]

object SQLSchema {
  def toSQLSchema[A]: SQLSchema[A] = macro SQLSchemaImpl.toSQLSchemaImpl[A]

  def implictFor[A](implicit sqlSchema: SQLSchema[A]): SQLSchema[A] = sqlSchema

  implicit object SQLSchemaInt extends RawSQLSchema[Int]("INTEGER")
  implicit object SQLSchemaString extends RawSQLSchema[String]("STRING")
}

====================================

I would like to do code like:

====================================

object Sub {
    implicit val sqlSchema: SQLSchema[Sub] = SQLSchema.toSQLSchema[Sub]
}
case class Sub(i:Int, s:String)

====================================

with
Sub.sqlSchema == RecordSQLSchema[Sub](List(
   "i" -> SQLSchema.SQLSchemaInt,
   "s" -> SQLSchema.SQLSchemaString,
))


Lets look the ugly part. If I try to generate a RawSQLSchema I can do:

====================================
object SQLSchemaImpl {
  def toSQLSchemaImplRaw[A: c.WeakTypeTag](c: blackbox.Context): c.Expr[SQLSchema[A]] = {
    import c.universe._

    val typeSymbol = weakTypeOf[A].typeSymbol

    val value = "not what I want"
    val codeRaw = q"RawSQLSchema[$typeSymbol]($value)"

    c.Expr[SQLSchema[A]](codeRaw)
  }
   ...
}

====================================

But this is not what I need. I would need the following code to work

====================================
object SQLSchemaImpl {
  def toSQLSchemaImplComplex[A: c.WeakTypeTag](c: blackbox.Context): c.Expr[SQLSchema[A]] = {
    import c.universe._

    val typeSymbol = weakTypeOf[A].typeSymbol

    val parts = for {
      member <- typeSymbol.typeSignature.members.toList
      if member.isTerm
      term = member.asTerm
      if term.isCaseAccessor
      if term.isGetter
      name = term.name.toString
      typeName = term.typeSignature.typeSymbol.fullName
    } yield q""" Tuple2($name, SQLSchema.implictFor[$typeName]) :: l"""

    val code1 = q"RecordSQLSchema[${weakTypeOf[A].typeSymbol}]($parts)"
    println(s" >>>>> code1: $code1")

//    val code2 = q"RecordSQLSchema[${weakTypeOf[A].typeSymbol}](...${List(parts)})"
//    println(s" >>>>> code2: $code2")

//    val code3 = q"RecordSQLSchema[${weakTypeOf[A].typeSymbol}](List.apply(...$parts))"
//    println(s" >>>>> code3: $code3")

//    val listApply = typeOf[List[SQLSchema[_]]].member(TermName("apply"))
//    val code4 = q"RecordSQLSchema[${weakTypeOf[A].typeSymbol}]($listApply ...$parts)"
//    println(s" >>>>> code4: $code4")

    c.Expr[SQLSchema[A]](code1)
  }
}
====================================

Whatever I tried failed :(

How can I build that List on the RecordSQLSchema?

Thanks for any help

Best Regards
Marcos

Rafał Krzewski

unread,
Apr 24, 2017, 9:17:51 AM4/24/17
to scala-user
Have you considered using Shapeless for that? It will do all the dirty macro work for you while you can concentrate on the interesting bits.
Here's an excellent short book that describes how to use Shapeless to do automatic typeclass derivation for your own types, which is exactly what you want, if I understood your use case correctly :)

Cheers,
Rafał
Reply all
Reply to author
Forward
0 new messages