在某些用例中,创建对象的副本很有用,该对象是一组案例类的案例类的实例,它们具有共同的特定值.
例如,让我们考虑以下案例类:
case class Foo(id: Option[Int])
case class Bar(arg0: String, id: Option[Int])
case class Baz(arg0: Int, id: Option[Int], arg2: String)
Run Code Online (Sandbox Code Playgroud)
然后copy可以在每个案例类实例上调用:
val newId = Some(1)
Foo(None).copy(id = newId)
Bar("bar", None).copy(id = newId)
Baz(42, None, "baz").copy(id = newId)
Run Code Online (Sandbox Code Playgroud)
type Copyable[T] = { def copy(id: Option[Int]): T }
// THIS DOES *NOT* WORK FOR CASE CLASSES
def withId[T <: Copyable[T]](obj: T, newId: Option[Int]): T =
obj.copy(id = newId)
Run Code Online (Sandbox Code Playgroud)
所以我创建了一个scala宏,它完成了这项工作(几乎):
import scala.reflect.macros.Context
object Entity {
import …Run Code Online (Sandbox Code Playgroud) 我昨晚熬夜试图弄清楚这个无形的问题,如果我不把它从胸前拿走,我担心它会吃掉我的晚上,所以这里就是这样.
在这个最小化版本中,我只是定义了一个类型类,它将递归转换为异构列表:
import shapeless._
trait DeepHLister[R <: HList] extends DepFn1[R] { type Out <: HList }
trait LowPriorityDeepHLister {
type Aux[R <: HList, Out0 <: HList] = DeepHLister[R] { type Out = Out0 }
implicit def headNotCaseClassDeepHLister[H, T <: HList](implicit
dht: DeepHLister[T]
): Aux[H :: T, H :: dht.Out] = new DeepHLister[H :: T] {
type Out = H :: dht.Out
def apply(r: H :: T) = r.head :: dht(r.tail)
}
}
object DeepHLister extends LowPriorityDeepHLister {
implicit …Run Code Online (Sandbox Code Playgroud) 我开始使用scala宏,它们很棒,但我遇到了typed(又是typechecked)和无类型Trees 之间的区别.
例如,c.eval由于某种原因,您无法使用类型检查的树进行调用.我在scala宏文档中找不到关于这个'typechecked'的文档(我知道他们仍然在努力,这可能是某些事情需要在某天添加).
对于一个Tree被类型检查来说意味着什么?为什么它们如此不同以至于显然c.eval无法处理类型检测Tree(反之则对我更有意义).
我想这可能是编译器101,但我没有采取这个过程:(任何解释或指向文章/文件的指针将不胜感激!
我想创建一个密封的抽象和案例类的宏生成层次结构.有一个与http://docs.scala-lang.org/overviews/macros/typemacros.html类似的例子,但现在已经过时了.这还有可能吗?
我认为为某些指定的语法生成类型安全的AST会非常强大.理想情况下,IDE可以解析所有类.
是否有可能(通过宏,某种形式的无形自动或其他形式)获得密封特征的子类列表:
如果我有一个宏tranforms代码如下:
(src: a.b.c.TestEntity) =>
{
z.y.TestTable(None)
}
Run Code Online (Sandbox Code Playgroud)
要匹配该AST的None部分,我可以使用提取器,例如:
object NoneExtractor {
def unapply(t: Tree): Boolean = t match {
case Select(Ident(scala), none) if scala.encoded == "scala" && none.encoded == "None" => true
case _ => false
}
}
Run Code Online (Sandbox Code Playgroud)
因为showRawAST的None部分看起来像:
Select(Ident(scala), None)
Run Code Online (Sandbox Code Playgroud)
然而,如果我想编写单元测试,NoneExtractor我不想编译和重建宏,并在宏编译的项目中托管测试.我想在宏的项目中对提取器进行单元测试,这表明运行时反射是可行的方法:
val t = reify {
(src: a.b.c.TestEntity) =>
{
z.y.TestTable(None)
}
}.tree
Run Code Online (Sandbox Code Playgroud)
然而树是完全不同的,在showRaw那棵树中看起来像是:
Ident(scala.None)
Run Code Online (Sandbox Code Playgroud)
这对于编写负面测试和检查宏的错误处理来说是个坏消息.您不能使用来自另一个项目的宏为宏编写负面测试,因为代码无法编译(并且您无法使用编译错误调试负面测试).
为什么在编译时反射和运行时反射之间,像None一样基本的表示形式如此不同?有没有办法在宏项目中创建可测试的树片段,这与在编译时反射期间传递给宏的AST相同?
我一直在使用Scala Macros并在宏中使用以下代码:
val fieldMemberType = fieldMember.typeSignatureIn(objectType) match {
case NullaryMethodType(tpe) => tpe
case _ => doesntCompile(s"$propertyName isn't a field, it must be another thing")
}
reify{
new TypeBuilder() {
type fieldType = fieldMemberType.type
}
}
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,我已经成功了c.universe.Type fieldMemberType.这表示对象中某个字段的类型.一旦我得到了,我想TypeBuilder在reify中创建一个新对象.TypeBuilder是一个带抽象参数的抽象类.这个抽象参数是fieldType.我希望这fieldType是我之前找到的类型.
运行此处显示的代码会返回一个fieldMemberType not found.有什么方法可以让我fieldMemberType在reify子句中工作吗?
我想编写一个Scala宏,它将一个case类的实例作为参数.可以传递给宏的所有对象都必须实现特定的标记特征.
以下代码段显示了标记特征和实现它的两个示例案例类:
trait Domain
case class Country( id: String, name: String ) extends Domain
case class Town( id: String, longitude: Double, latitude: Double ) extends Domain
Run Code Online (Sandbox Code Playgroud)
现在,我想使用宏编写以下代码,以避免运行时反射的严重性及其线程不安全:
object Test extends App {
// instantiate example domain object
val myCountry = Country( "CH", "Switzerland" )
// this is a macro call
logDomain( myCountry )
}
Run Code Online (Sandbox Code Playgroud)
宏logDomain在不同的项目中实现,看起来类似于:
object Macros {
def logDomain( domain: Domain ): Unit = macro logDomainMacroImpl
def logDomainMacroImpl( c: Context )( domain: c.Expr[Domain] ): c.Expr[Unit] = { …Run Code Online (Sandbox Code Playgroud) 我有以下宏定义一个类并返回该类的实例(使用Scala 2.10.2和宏插件):
def test[T] = macro testImpl[T]
def testImpl[T : c.WeakTypeTag](c: Context): c.Expr[Any] = {
import c.universe._
val className = newTypeName("Test")
c.Expr { q"""
class $className {
def method = 1
}
new $className
"""}
}
Run Code Online (Sandbox Code Playgroud)
当我调用宏时:
case class Cat(name: String)
val t = test[Cat].method
Run Code Online (Sandbox Code Playgroud)
我收到以下错误:
method method in class Test cannot be accessed in Test
val t = test[Cat].method
^
Run Code Online (Sandbox Code Playgroud)
我的总体目标是使用吸血鬼方法并使用准引号来描述生成的类.我该如何解决这个错误?
我只是通过将scala-reflect.jar库作为pom中的依赖项包含普通scala-2.10宏在maven项目中工作,但是我需要打开宏天堂呢?我使用的是scala-2.10和scala-maven-plugin-3.1.5.
scala ×10
scala-macros ×10
macros ×2
scala-2.10 ×2
shapeless ×2
case-class ×1
maven ×1
typechecking ×1