是否可以编写一个scala宏,其返回类型取决于参数? [英] Is it possible to write a scala macro whose returntype depends on argument?

查看:64
本文介绍了是否可以编写一个scala宏,其返回类型取决于参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于DSL,我希望能够执行以下操作:

For a DSL I would like to be able to do something like:

object Creator { 
   def create[T](s :String) :Foo[T] = macro createImpl[T]
   def createImpl[T](c :Context)(s :c.Expr[String]) : c.Expr[Foo[T]] = {
        reify(new Foo[Any]())
   }
 }

我的问题是将reify中的Any替换为可以返回正确参数化版本的东西.

My problem is to replace the Any in reify with something that will return the correctly parametrized version.

(上面我使用了字符串参数,但是在最终版本中,我计划使用T类的伴随对象作为标记来了解Function1 [T,Unit]的参数类型)

(above I use a string argument, but in the final version I plan to use the companion object of class T as a marker to know the argument-type of a Function1[T,Unit])

推荐答案

您需要: a)写new Foo[T]()而不是new Foo[Any]()(简单) b)通过使用上下文绑定:[T: c.AbsTypeTag]声明参数T,在宏中传递类型T的表示形式,即类型AbsTypeTag[T]的值.

You need to: a) write new Foo[T]() instead of new Foo[Any]() (easy) b) pass in the macro a representation of type T, namely, a value of type AbsTypeTag[T], by declaring the parameter T using a context bound: [T: c.AbsTypeTag].

这是我在Scala 2.10.0-M7中测试的代码. 编辑.在2.10.0-RC1中,AbsTypeTag已重命名为WeakTypeTag.关于宏和类型标签的其他所有内容都保持不变.

Here's code which I tested in Scala 2.10.0-M7. Edit. In 2.10.0-RC1 AbsTypeTag has been renamed to WeakTypeTag. Everything else about macros and type tags remains the same.

Creator.scala:

Creator.scala:

import language.experimental.macros
import reflect.macros.Context
class Foo[T]
object Creator {
  def create[T](s: String): Foo[T] = macro createImpl[T]
  def createImpl[T: c.AbsTypeTag](c: Context)(s: c.Expr[String]): c.Expr[Foo[T]] = {
    import c.universe._
    reify(new Foo[T]())
  }
}

MacroClient.scala:

MacroClient.scala:

object Main extends App {
  println (Creator.create[Int](""))
}

请注意,如果省略type参数,则会出现奇怪的错误:

Note that if you omit the type parameter, you will get a weird error:

scala> Creator.create[Int]("")
res2: Foo[Int] = Foo@4888884e

scala> Creator.create("")
<console>:12: error: macro has not been expanded
              Creator.create("")
                      ^

您还写:

(以上我使用了字符串参数,但在最终版本中,我计划使用 T类的伴随对象作为标记来了解参数类型 Function1 [T,Unit])

(above I use a string argument, but in the final version I plan to use the companion object of class T as a marker to know the argument-type of a Function1[T,Unit])

但是,如果我做对了,这听起来是个坏主意.而不是编写Creator.create[T](otherArgs),调用语法将类似于Creator.create(T, otherArgs),并不是很大的优势(如果有的话).但您甚至无法获得后一种语法:如果class Aobject A是同伴,则它们的类型不相关:第一个具有类型A,第二个具有类型A.type,其中A是同伴对象,而不是类A的类型.

but if I get it right, this sounds like a bad idea. Instead of writing Creator.create[T](otherArgs), the call syntax would be something like Creator.create(T, otherArgs), not a big advantage (if any). But you can't even get the latter syntax: if class A and object A are companions, their types are not related: the first has type A, the second has type A.type where A is the companion object and not the type of class A.

更新:如果可以控制Foo,如何使Creator create Foo语法起作用并返回Foo的实例. 因为您询问的是reifyAny类型参数,所以我假设您是在询问类型参数.仅当您希望Creator.create static 返回类型为T而不是Any时才有意义;否则,您应该澄清您的问题.

Update: how to get the Creator create Foo syntax to work and return an instance of Foo, if you have control over Foo. Since you ask about the Any type argument to reify, I assume you are asking about the type argument. That makes only sense if you want the static return type of Creator.create to be T and not Any; otherwise, you should clarify your question.

这里的问题与宏关系不大. Creator create Foo将对象Foo传递给Creator.create,对于给定的Foo.type,该对象的声明需要通过类型表达式来表示类型Foo. Scala中的类型表达式非常有限-例如,它们不能使用反射.但是给定类型后,他们可以选择其类型成员.

The problem here has little to do with macros. Creator create Foo passes the object Foo to Creator.create, whose declaration needs to express, given Foo.type, the type Foo through a type expression. Type expressions in Scala are quite limited - they can't use reflection, for instance. But given a type, they can select its type members.

trait Companion[Class]
//How to declare a companion
class Foo
object Foo extends Companion[Foo]
/*I'm cheating: an implementation of Companion does not need to be a true Companion. You can add documentation to explain how Companion is supposed to be used. */
object Bar extends Companion[Foo]
//But this is also useful - you can't create your own companion objects for pre-existing types, but you can still create the right instances of Companion:
object pInt extends Companion[Int]
object Creator {
  //T with Companion[S] is needed to workaround a type inference bug (SI-5298) and allow S to be correctly inferred.
  def create[S, T <: Companion[S]](obj: T with Companion[S]): S = ???
}

这是有限的,因为您需要更改伴随对象,但是我敢肯定您做得更好.我不知道在类型表达式中(在声明create的返回类型时可以使用什么代替S)从同伴对象到其关联的类类型的方法,但我认为没有.

This is limited since you need to alter the companion object, but I'm pretty sure you can't do better. I know no way, in a type expression (what you can use in place of S when declaring the return type of create) of getting from a companion object to its associated class type in general, and I don't think there's one.

现在,更改以上内容以使用宏非常简单:

Now, changing the above to use macros is straightforward:

import language.experimental.macros
import reflect.macros.Context
class Foo[T]
object Creator {
  //T with Companion[S] is needed to workaround a type inference bug (SI-5298) and allow S to be correctly inferred.
  def create[S, T <: Companion[S]](obj: T with Companion[S]): Foo[S] = macro createImpl[S, T]
  def createImpl[S: c.AbsTypeTag, T <: Companion[S]: c.AbsTypeTag](c: Context)(obj: c.Expr[T with Companion[S]]): c.Expr[Foo[S]] = {
    import c.universe._
    reify(new Foo[S]())
  }
}

这篇关于是否可以编写一个scala宏,其返回类型取决于参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆