Macros: path dependent type inference confusion - scala

I tried to simplify the creation of ASTs, but got a weird error message:
case class Box(i: Int)
object M {
import language.experimental.macros
import scala.reflect.makro.Context
case class meth(obj: String, method: String)(implicit val c: Context) {
import c.universe._
def apply(xs: Tree*) =
Apply(Select(Ident(obj), newTermName(method)), xs.toList)
}
def box(n: Int): Box = macro boxImpl
def boxImpl(c: Context)(n: c.Expr[Int]): c.Expr[Box] = {
import c.universe._
implicit val cc: c.type = c
n.tree match {
case arg # Literal(Constant(_)) =>
meth("Box", "apply").apply(arg)
}
}
}
Error:
<console>:26: error: type mismatch;
found : c.universe.Literal
required: _2.c.universe.Tree where val _2: M.meth
possible cause: missing arguments for method or constructor
meth("Box", "apply").apply(arg)
^
Is it possible to infer the correct types into class meth? Or is there a workaround for the problem?
EDIT: Based on #retronyms answer I got this to work:
object M {
import language.experimental.macros
import scala.reflect.makro.Context
def meth(implicit c: Context) = new Meth[c.type](c)
class Meth[C <: Context](val c: C) {
import c.universe._
def apply(obj: String, method: String, xs: Tree*) =
Apply(Select(Ident(obj), newTermName(method)), xs.toList)
}
def box(n: Int): Box = macro boxImpl
def boxImpl(c: Context)(n: c.Expr[Int]): c.Expr[Box] = {
import c.universe._
implicit val cc: c.type = c
n.tree match {
case arg # Literal(Constant(_)) =>
c.Expr(meth.apply("Box", "apply", arg))
}
}
}

Constructors are not currently allowed to have dependent method types (SI-5712). It's on the radar to be fixed, hopefully for 2.10.1 or 2.11.
In the meantime, you can follow the pattern I used in macrocosm to reuse code within macro implementations.

Related

How to use ClassTag in scala macros implemented for type

I wrote a macros, that reads class fields:
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
object ArrayLikeFields {
def extract[T]: Set[String] = macro extractImpl[T]
def extractImpl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[Set[String]] = {
import c.universe._
val tree = weakTypeOf[T].decls
.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
}
.map(y => y.paramLists.headOption.getOrElse(Seq.empty))
.getOrElse(Seq.empty)
.map(s => q"${s.name.decodedName.toString}")
c.Expr[Set[String]] {
q"""Set(..$tree)"""
}
}
}
I'm able to compile and run it for concrete type:
object Main extends App {
case class Person(name:String)
val res: Set[String] = ArrayLikeFields.extract[Person]
}
But i want use it with generic types like that:
object Lib {
implicit class SomeImplicit(s: String) {
def toOrgJson[T]: JSONObject = {
val arrayLikeFields: Set[String] = ArrayLikeFields.extract[T]
//some code, that uses fields, etc
null
}
}
}
Compilation error:
Error:(14, 65) type mismatch; found :
scala.collection.immutable.Set[Nothing] required: Set[String] Note:
Nothing <: String, but trait Set is invariant in type A. You may wish
to investigate a wildcard type such as _ <: String. (SLS 3.2.10)
val arrayLikeFields: Set[String] = ArrayLikeFields.extract[T]
I can't understund that. How can I solve my problem?
upd
I read scala 2.10.2 calling a 'macro method' with generic type not work about materialisation, but i have no instance of class
Try approach with materializing a type class like in 1
object Main extends App {
case class Person(name:String)
val res: Set[String] = ArrayLikeFields.extract[Person] //Set(name)
import Lib._
"abc".toOrgJson[Person] // prints Set(name)
}
object Lib {
implicit class SomeImplicit(s: String) {
def toOrgJson[T: ArrayLikeFields.Extract]: JSONObject = {
val arrayLikeFields: Set[String] = ArrayLikeFields.extract[T]
//some code, that uses fields, etc
println(arrayLikeFields) //added
null
}
}
}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
object ArrayLikeFields {
def extract[T](implicit extr: Extract[T]): Set[String] = extr()
trait Extract[T] {
def apply(): Set[String]
}
object Extract {
implicit def materializeExtract[T]: Extract[T] = macro materializeExtractImpl[T]
def materializeExtractImpl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[Extract[T]] = {
import c.universe._
val tree = weakTypeOf[T].decls
.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
}
.map(y => y.paramLists.headOption.getOrElse(Seq.empty))
.getOrElse(Seq.empty)
.map(s => q"${s.name.decodedName.toString}")
c.Expr[Extract[T]] {
q"""new ArrayLikeFields.Extract[${weakTypeOf[T]}] {
override def apply(): _root_.scala.collection.immutable.Set[_root_.java.lang.String] =
_root_.scala.collection.immutable.Set(..$tree)
}"""
}
}
}
}
Actually, I don't think you need whitebox macros here, blackbox ones should be enough. So you can replace (c: whitebox.Context) with (c: blackbox.Context).
By the way, the same problem can be solved with Shapeless rather than macros (macros work in Shapeless under the hood)
object Main extends App {
case class Person(name:String)
val res: Set[String] = ArrayLikeFields.extract[Person] //Set(name)
}
object ArrayLikeFields {
def extract[T: Extract]: Set[String] = implicitly[Extract[T]].apply()
trait Extract[T] {
def apply(): Set[String]
}
object Extract {
def instance[T](strs: Set[String]): Extract[T] = () => strs
implicit def genericExtract[T, Repr <: HList](implicit
labelledGeneric: LabelledGeneric.Aux[T, Repr],
extract: Extract[Repr]
): Extract[T] = instance(extract())
implicit def hconsExtract[K <: Symbol, V, T <: HList](implicit
extract: Extract[T],
witness: Witness.Aux[K]
): Extract[FieldType[K, V] :: T] =
instance(extract() + witness.value.name)
implicit val hnilExtract: Extract[HNil] = instance(Set())
}
}
The answer on the linked question, scala 2.10.2 calling a 'macro method' with generic type not work , also applies here.
You are trying to solve a run-time problem with a compile-time macro, which is not possible.
The called method toOrgJson[T] cannot know the concrete type that T represents at compile time, but only gets that information at run-time. Therefore, you will not be able to do any concrete operations on T (such as listing its fields) at compile-time, only at run-time.
You can implement an operation like ArrayLikeFields.extract[T] at run-time using Reflection, see e.g. Get field names list from case class
I don't have a very solid understanding of Macros, but it seems that the compiler does not understand that the return type of the macro function is Set[String].
The following trick worked for me in scala 2.12.7
def toOrgJson[T]: JSONObject = {
val arrayLikeFields: Set[String] = ArrayLikeFields.extract[T].map(identity[String])
//some code, that uses fields, etc
null
}
EDIT
Actually to get a non empty Set T needs an upper bound such as T <: Person... and that is not what you wanted...
Leaving the answer here since the code does compile, and it might help someone in the direction of an answer

enable implicit import for runtime type in scala check. "could not find implicit value for parameter"

I'm testing my own home-brewed Monoid classes in scala using the ScalaCheck library and ScalaTest
when attempting to implement DRY tests, I get the implicit error in the title:
Error:(16, 12) could not find implicit value for parameter arbA: org.scalacheck.Arbitrary[A]
forAll { (a: A) =>
^
here is the implementation of intAddition Monoid:
trait Monoid[A] {
def op(a1: A, a2: A): A
def zero: A
}
object Monoid {
...
val intAddition: Monoid[Int] = new Monoid[Int] {
override def op(a1: Int, a2: Int): Int = a1 + a2
override def zero: Int = 0
}
...
}
And the test suite:
import org.fpinscala.monoids.Monoid._
import org.fpinscala.testutils.UnitSpec
import org.scalatest.prop.PropertyChecks
import org.scalacheck.Arbitrary._
import scala.language.implicitConversions
class MonoidSpec extends UnitSpec with PropertyChecks {
def assertIdentityBehaviour[A](M: Monoid[A]): Unit = {
import M._
forAll { (a: A) =>
op(zero, a) should be(a)
op(a, zero) should be(a)
}
}
behavior of "intAdditionMonoid"
it should "obey identity laws" in {
assertIdentityBehaviour(intAddition)
}
}
This code compiles but fails at runtime (runtime type erasure?).
Is what I'm trying to achieve possible in Scala?
This code compiles
It doesn't; the error you give is a compilation error. It should be fixed by adding the implicit parameter it complains about:
def assertIdentityBehaviour[A](M: Monoid[A])(implicit arbA: Arbitrary[A]) = ...
// or equivalently, def assertIdentityBehaviour[A: Arbitrary](M: Monoid[A]) = ...
You are calling assertIdentityBehaviour only with A for which the parameter is available, but the error is in its definition.

Can Scala macros be defined inside a class (as methods of that class)?

I need Scala macros (reify, quasiquote, macro impl) for my Scala assertions library.
I want to be able to do this:
object1.assertEquals(object2) // success: object1 = object2
Or like this:
3.assertEquals(1 + 1) // failure: 1 + 1 /= 3
Can Scala macros be defined inside an implicit class?
The implementation has to be in an object or a macro bundle, but the method that is implemented by the macro can be in an implicit class.
Notice val self = q"${c.prefix}.self" that is needed to get the reference to the object that is wrapped by the implicit class.
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
object assertions {
implicit class AssertEquals[T](val self: T) extends AnyVal {
def assertEquals(other: T): Unit = macro AssertionMacros.assertEquals[T]
}
object AssertionMacros {
def assertEquals[T](c: Context)(other: c.Tree): c.Tree = {
import c.universe._
val self = q"${c.prefix}.self"
q"_root_.scala.Predef.assert($self == $other)"
}
}
}
Usage:
scala> import assertions._
import assertions._
scala> "foo" assertEquals "foo"
scala> "foo" assertEquals "bar"
java.lang.AssertionError: assertion failed
at scala.Predef$.assert(Predef.scala:151)
... 43 elided
//ed : write package
package so
trait AssertEquals[T, V] {
def assertEquals(t: T, v: V): Boolean
}
object AssertEquals {
implicit def assertEquals[T, V]: AssertEquals[T, V] = macro impl[T, V]
implicit class WithAssertEquals[T](t: T) {
def assertEquals[V](v: V)(implicit assertEquals: AssertEquals[T, V]): Boolean = assertEquals.assertEquals(t, v)
}
def impl[T: c.WeakTypeTag, V: c.WeakTypeTag](c: Context) = {
import c.universe._
val _t = c.weakTypeOf[T]
val _v = c.weakTypeOf[V]
//edit 2 : use symbolOf instead typeOf
q"""
{
new ${symbolOf[so.AssertEquals[_, _]]}[${_t},${_v}]{
def assertEquals(t: ${_t}, v: ${_v}): Boolean = t == v
}
}
"""
}
}
//test
import AssertEquals.WithAssertEquals
assert(1.assertEquals(2) == false)
assert(2.assertEquals(2) == true)
assert("a".assertEquals("a") == true)
assert("a".assertEquals("b") == false)
assert("a".assertEquals(1) == false)

Calling function with implicit parameter from quasioquote

For some reason every time I try to call a function with an implicit parameter from quasiquotes, it fails with
Can't unquote x.universe.Tree, consider providing an implicit instance of Liftable[x.universe.Tree]
What's wrong with that? Am I not allowed to do that? I couldn't find anywhere where it says I can't do it
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
object Foo {
def foo: Unit = macro fooImpl
def fooImpl(c: whitebox.Context): c.Expr[Unit] = {
import c.universe._
implicit val x = c
val res = q"$sysoutFQN"
c.Expr(res)
}
def sysoutFQN(implicit c: whitebox.Context): c.universe.Tree = {
import c.universe._
q"java.lang.System.out.println()"
}
}
because path depend so c.universe.Tree != x.universe.Tree
so you need write x type
implicit val x: c.type = c

Writing a pattern matching macro

I need some help writing a macro that produces a pattern match.
This is as far as I got:
import scala.reflect.macros.Context
import language.experimental.macros
trait JsValue
object SealedTraitFormat {
def writesImpl[A: c.WeakTypeTag](c: Context)(value: c.Expr[A]): c.Expr[JsValue] = {
val aTpeW = c.weakTypeOf[A]
val aClazz = aTpeW.typeSymbol.asClass
require(aClazz.isSealed, s"Type $aTpeW is not sealed")
val subs = aClazz.knownDirectSubclasses
require(subs.nonEmpty , s"Type $aTpeW does not have known direct subclasses")
import c.universe._
val cases = subs.toList.map { sub =>
val pat = Bind(newTermName("x"), Typed(Ident("_"),
c.reifyRuntimeClass(sub.asClass.toType)))
val body = Ident("???") // TODO
CaseDef(pat, body)
}
val m = Match(value.tree, cases)
c.Expr[JsValue](m)
}
}
trait SealedTraitFormat[A] {
def writes(value: A): JsValue = macro SealedTraitFormat.writesImpl[A]
}
Here is an example:
sealed trait Foo
case class Bar() extends Foo
case class Baz() extends Foo
object FooFmt extends SealedTraitFormat[Foo] {
val test = writes(Bar())
}
The current error is:
[warn] .../FooTest.scala:8: fruitless type test: a value of type play.api.libs.json.Bar cannot also be a Class[play.api.libs.json.Bar]
[warn] val test = writes(Bar())
[warn] ^
[error] .../FooTest.scala:8: pattern type is incompatible with expected type;
[error] found : Class[play.api.libs.json.Bar](classOf[play.api.libs.json.Bar])
[error] required: play.api.libs.json.Bar
[error] val test = writes(Bar())
[error] ^
(note that play.api.libs.json is my package, so that's correct). I'm not sure what to make of this error...
The expanded macro should look like this
def writes(value: Foo): JsValue = value match {
case x: Bar => ???
case x: Baz => ???
}
It appears to me, that it probably looks like case x: Class[Bar] => ??? now. So my guess is I need to use reifyType instead of reifyRuntimeClass. Basically, how do I get the tree from a Type?
The following seems to work, or at least compile:
val cases = subs.toList.map { sub =>
val pat = Bind(newTermName("x"), Typed(Ident("_"), Ident(sub.asClass)))
val body = reify(???).tree // TODO
CaseDef(pat, body)
}