我试图调用Shapeless从内宏quasiquote用Scala,我没有得到什么,我想获得.
我的宏不会返回任何错误,但它不会扩展Witness(fieldName)到Witness.Lt[String]
val implicits = schema.fields.map { field =>
val fieldName:String = field.name
val fieldType = TypeName(field.valueType.fullName)
val in = TermName("implicitField"+fieldName)
val tn = TermName(fieldName)
val cc = TermName("cc")
q"""implicit val $in = Field.apply[$className,$fieldType](Witness($fieldName), ($cc: $className) => $cc.$tn)"""
}
Run Code Online (Sandbox Code Playgroud)
这是我的Field定义:
sealed abstract class Field[CC, FieldName] {
val fieldName: String
type fieldType
// How to extract this field
def get(cc : CC) : fieldType
}
object Field {
// fieldType …Run Code Online (Sandbox Code Playgroud) 假设我们要编写一个定义具有某些类型成员或方法的匿名类的宏,然后创建该类的实例,该类通过这些方法静态地键入为结构类型等.这可以通过2.10中的宏系统实现. 0,类型成员部分非常容易:
object MacroExample extends ReflectionUtils {
import scala.language.experimental.macros
import scala.reflect.macros.Context
def foo(name: String): Any = macro foo_impl
def foo_impl(c: Context)(name: c.Expr[String]) = {
import c.universe._
val Literal(Constant(lit: String)) = name.tree
val anon = newTypeName(c.fresh)
c.Expr(Block(
ClassDef(
Modifiers(Flag.FINAL), anon, Nil, Template(
Nil, emptyValDef, List(
constructor(c.universe),
TypeDef(Modifiers(), newTypeName(lit), Nil, TypeTree(typeOf[Int]))
)
)
),
Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
))
}
}
Run Code Online (Sandbox Code Playgroud)
(提供我方法ReflectionUtils的便利特性在哪里constructor.)
这个宏允许我们将匿名类的类型成员的名称指定为字符串文字:
scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6
Run Code Online (Sandbox Code Playgroud)
请注意,它是适当的键入.我们可以确认一切都按预期工作:
scala> implicitly[res0.T =:= Int] …Run Code Online (Sandbox Code Playgroud) 我将从一个例子开始.这里的List.fill元组与Scala 2.10中的宏相当:
import scala.language.experimental.macros
import scala.reflect.macros.Context
object TupleExample {
def fill[A](arity: Int)(a: A): Product = macro fill_impl[A]
def fill_impl[A](c: Context)(arity: c.Expr[Int])(a: c.Expr[A]) = {
import c.universe._
arity.tree match {
case Literal(Constant(n: Int)) if n < 23 => c.Expr(
Apply(
Select(Ident("Tuple" + n.toString), "apply"),
List.fill(n)(a.tree)
)
)
case _ => c.abort(
c.enclosingPosition,
"Desired arity must be a compile-time constant less than 23!"
)
}
}
}
Run Code Online (Sandbox Code Playgroud)
我们可以使用以下方法:
scala> TupleExample.fill(3)("hello")
res0: (String, String, String) = (hello,hello,hello)
Run Code Online (Sandbox Code Playgroud)
这个家伙在几个方面都是一只奇怪的鸟.首先,arity参数必须是一个文字整数,因为我们需要在编译时使用它.在Scala的早期版本中,(根据我所知),方法甚至无法判断其中一个参数是否是编译时文字.
其次, …
所以我有这个宏:
import language.experimental.macros
import scala.reflect.macros.Context
class Foo
class Bar extends Foo { def launchMissiles = "launching" }
object FooExample {
def foo: Foo = macro foo_impl
def foo_impl(c: Context): c.Expr[Foo] =
c.Expr[Foo](c.universe.reify(new Bar).tree)
}
Run Code Online (Sandbox Code Playgroud)
我已经说了三次我想要foo返回一个Foo,但我可以做以下(在2.10.0-RC3中):
scala> FooExample.foo
res0: Bar = Bar@4118f8dd
scala> res0.launchMissiles
res1: String = launching
Run Code Online (Sandbox Code Playgroud)
如果我删除任何一个上的类型参数,也会发生同样的事情c.Expr.如果我真的想确保无论谁打电话foo都看不到他们正在获得Bar,我必须在树本身添加类型归属.
这实际上非常棒 - 例如,我可以将宏指向某种类型的模式,并Vocabulary使用表示词汇表中的术语的成员方法创建某个类的匿名子类,这些将在返回的对象上可用.
我想知道我到底在做什么,所以我有几个问题.首先,该foo方法实际上的返回类型是什么?它是否仅适用于(可选)文档?它明确地约束了返回类型(例如,Int在这种情况下我无法将其更改为),如果我将其完全删除,则会出现如下错误:
scala> FooExample.foo
<console>:8: error: type mismatch;
found : Bar
required: Nothing
FooExample.foo …Run Code Online (Sandbox Code Playgroud) 在哪里我可以学习如何构建Scala的宏生成的AST?
Scaladoc不像我想的那样有用.例如:
abstract def Apply(sym: Universe.Symbol, args: Universe.Tree*): Universe.Tree
A factory method for Apply nodes.
Run Code Online (Sandbox Code Playgroud)
但是我如何弄清楚Apply节点是什么?我在哪里可以找到AST的节点类型列表,以及它们如何组合在一起?
我只是想知道是否有可能在Scala中迭代密封的特征?如果没有,为什么不可能?由于特性是密封的,应该可以吗?
我想做的是这样的:
sealed trait ResizedImageKey {
/**
* Get the dimensions to use on the resized image associated with this key
*/
def getDimension(originalDimension: Dimension): Dimension
}
case class Dimension(width: Int, height: Int)
case object Large extends ResizedImageKey {
def getDimension(originalDimension: Dimension) = Dimension(1000,1000)
}
case object Medium extends ResizedImageKey{
def getDimension(originalDimension: Dimension) = Dimension(500,500)
}
case object Small extends ResizedImageKey{
def getDimension(originalDimension: Dimension) = Dimension(100,100)
}
Run Code Online (Sandbox Code Playgroud)
通过给枚举值赋予实现,我可以用Java完成.Scala中有同等的东西吗?
我有一个使用了SBT管理斯卡拉项目通常SBT项目布局斯卡拉用宏,即项目,包含宏的主要项目,是实际应用和依赖于宏观子项目子项目.宏是宏注释,实质上是为常规类生成伴随对象.生成的伴随对象在其他成员中声明了apply/unapply方法.
我使用sbt-idea插件生成相应的IntelliJ IDEA项目,并使用IDEA的sbt-plugin中的sbt控制台来编译和运行我的Scala应用程序.
一切都或多或少都有效,除了生成的伴随对象,更重要的是它们的成员,如apply/unapply,IDEA无法识别.因此,我在任何地方都得到了一条波浪线,例如,一种应用方法.
我的设置是IntelliJ IDEA CE 133.471,在Windows 7 x64上使用插件SBT 1.5.1和Scala 0.28.363.
如何让IntelliJ IDEA识别由Scala宏生成的代码(类,对象,方法......)(准确地说是宏注释)?
是否已知其他IDE(例如Eclipse)在这样的设置中工作得更好?
这个问题(不太详细)基本上问了同样的问题,但还没有得到答复(2014-02-26).
根据JetBrains开发人员的说法,我要求的功能是他们的长期待办事项清单,但不会很快实施(2014-03-05).
我正在用Scala宏替换Java程序中的一些代码生成组件,并且运行Java虚拟机对单个方法生成的字节代码大小的限制(64千字节).
例如,假设我们有一个大型的XML文件,它表示我们想要在程序中使用的从整数到整数的映射.我们希望避免在运行时解析此文件,因此我们将编写一个宏,它将在编译时进行解析并使用该文件的内容来创建方法的主体:
import scala.language.experimental.macros
import scala.reflect.macros.Context
object BigMethod {
// For this simplified example we'll just make some data up.
val mapping = List.tabulate(7000)(i => (i, i + 1))
def lookup(i: Int): Int = macro lookup_impl
def lookup_impl(c: Context)(i: c.Expr[Int]): c.Expr[Int] = {
import c.universe._
val switch = reify(new scala.annotation.switch).tree
val cases = mapping map {
case (k, v) => CaseDef(c.literal(k).tree, EmptyTree, c.literal(v).tree)
}
c.Expr(Match(Annotated(switch, i.tree), cases))
}
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,编译的方法将超过大小限制,但不是一个很好的错误说,我们给了一个巨大的堆栈跟踪与大量的调用,TreePrinter.printSeq并被告知我们已经杀死了编译器.
我有一个解决方案,涉及将案例拆分为固定大小的组,为每个组创建一个单独的方法,并添加一个顶级匹配,将输入值调度到适当的组的方法.它可以工作,但它很不愉快,而且我不想每次编写宏时都不必使用这种方法,其中生成的代码的大小取决于某些外部资源.
有没有更清洁的方法来解决这个问题?更重要的是,有没有办法更优雅地处理这种编译器错误?我不喜欢库用户得到一个难以理解的"该条目似乎已经杀死编译器"错误消息的想法只是因为宏正在处理的某些XML文件已超过一些(相当低的)大小阈值.
假设我有很多类似的数据类.这是一个示例类User,定义如下:
case class User (name: String, age: Int, posts: List[String]) {
val numPosts: Int = posts.length
...
def foo = "bar"
...
}
Run Code Online (Sandbox Code Playgroud)
我感兴趣的是自动创建一个方法(在编译时),Map该方法以在运行时调用每个字段名称时将其映射到其值的方式返回.对于上面的例子,让我们说我的方法被调用toMap:
val myUser = User("Foo", 25, List("Lorem", "Ipsum"))
myUser.toMap
Run Code Online (Sandbox Code Playgroud)
应该回来
Map("name" -> "Foo", "age" -> 25, "posts" -> List("Lorem", "Ipsum"), "numPosts" -> 2)
Run Code Online (Sandbox Code Playgroud)
你会如何用宏来做到这一点?
这就是我所做的:首先,我创建了一个Model类作为我所有数据类的超类,并在那里实现了这样的方法:
abstract class Model {
def toMap[T]: Map[String, Any] = macro toMap_impl[T]
}
class User(...) extends Model {
...
}
Run Code Online (Sandbox Code Playgroud)
然后我在一个单独的Macros对象中定义了一个宏实现: …
这个问题与我之前的问题的动机相似(虽然它是关于我在不同背景下遇到的问题).
在没有quasiquotes的情况下,我可以非常轻松地在函数文字上进行模式匹配:
import scala.reflect.macros.Context
import scala.language.experimental.macros
object QQExample {
def funcDemo(f: Int => String) = macro funcDemo_impl
def funcDemo_impl(c: Context)(f: c.Expr[Int => String]) = {
import c.universe._
f.tree match {
case Function(ps, body) => List(ps, body) foreach println
case _ => c.abort(
c.enclosingPosition,
"Must provide a function literal."
)
}
c.literalUnit
}
}
Run Code Online (Sandbox Code Playgroud)
其工作方式如下:
scala> QQExample.funcDemo((a: Int) => a.toString)
List(val a: Int = _)
a.toString()
Run Code Online (Sandbox Code Playgroud)
现在假设我想使用quasiquotes更灵活地进行相同类型的匹配.以下内容也将匹配该功能,并打印出我们期望的内容.
case q"($x: $t) => $body" => List(x, t, body) foreach …Run Code Online (Sandbox Code Playgroud) scala-macros ×10
scala ×9
macros ×6
scala-2.10 ×3
case-class ×1
compilation ×1
enumeration ×1
java ×1
jvm ×1
sealed ×1
shapeless ×1
types ×1