我正在尝试在 dotty 中学习元编程。具体编译时间代码生成。我认为通过构建一些东西来学习是一种很好的方法。所以我决定制作一个 CSV 解析器,它将行解析为案例类。我想使用dotty宏来生成解码器
trait Decoder[T]{
def decode(str:String):Either[ParseError, T]
}
object Decoder {
inline given stringDec as Decoder[String] = new Decoder[String] {
override def decode(str: String): Either[ParseError, String] = Right(str)
}
inline given intDec as Decoder[Int] = new Decoder[Int] {
override def decode(str: String): Either[ParseError, Int] =
str.toIntOption.toRight(ParseError(str, "value is not valid Int"))
}
inline def forType[T]:Decoder[T] = ${make[T]}
def make[T:Type](using qctx: QuoteContext):Expr[Decoder[T]] = ???
}
Run Code Online (Sandbox Code Playgroud)
我已经为Int&提供了基本的解码器String,现在我正在寻找def make[T:Type]方法的指导。如何T在此方法中迭代案例类的参数列表?有没有推荐的方法或模式来做到这一点?
我正在制作一个通过反射字段值设置的 Scala 应用程序。这工作正常。
但是,为了设置字段值,我需要一个创建的实例。如果我有一个带有空构造函数的类,我可以使用 classOf[Person].getConstructors 轻松做到这一点....
但是,当我尝试使用具有非空构造函数的 Case 类执行此操作时,它不起作用。我拥有所有字段名称及其值,以及我需要创建的对象类型。我可以用我所拥有的以某种方式实例化 Case Class 吗?
我唯一没有的是来自 Case Class 构造函数的参数名称,或者一种在没有参数的情况下创建它然后通过反射设置值的方法。
我们来看例子。
我有以下
case class Person(name : String, age : Int)
class Dog(name : String) {
def this() = {
name = "Tony"
}
}
class Reflector[O](obj : O) {
def setValue[F](propName : String, value : F) = ...
def getValue(propName : String) = ...
}
//This works
val dog = classOf[Dog].newInstance()
new Reflector(dog).setValue("name", "Doggy")
//This doesn't
val person = classOf[Person].newInstance //Doesn't work
val ctor …Run Code Online (Sandbox Code Playgroud) 我想创建一个注释或特征,在编译时动态地根据现有字段向对象添加方法.虽然我对类级别的东西很感兴趣,但我也会使用字段级注释(或其他更细粒度的东西).
一个较旧的堆栈溢出问题询问了Scala的实现细节,@BeanProperty回答说:"它是一个编译器插件,但宏也可能允许你这样做".鉴于Scala 2.10中的官方(如果是实验性的)宏版本,现在这种功能是否可行?
我有一个表示包,对象和类的符号列表,并希望在宏上下文中导入它们.
对于包和对象,这将意味着通配符导入,对于类,它将意味着"标准"导入.
给定一个List[Symbol]由some.package,some.Class和some.Object,我将如何正确导入这些,我怎么能决定是否"标准"或通配符导入,需要使用?
我目前的做法是:
def importPackageOrModuleOrClass(sym: Symbol): Import =
if (sym.isPackage || sym.isModule) // e. g. import scala._, scala.Predef
gen.mkWildcardImport(sym)
else // e. g. import java.lang.String
gen.mkImport(sym.enclosingPackage, sym.name, sym.name) // <--- ?????
Run Code Online (Sandbox Code Playgroud)
包/模块导入有效,但类导入虽然看起来不正确.
有没有一种方法,以返回List的TypeSymbol使用宏包项下的每个类?
我想要实现的是编写一个宏,它给出了与此列表等效的东西:
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> case class MyClass1()
defined class MyClass1
scala> case class MyClass2()
defined class MyClass2
scala> val typeSymbols = List(typeOf[MyClass1].typeSymbol, typeOf[MyClass2].typeSymbol)
typeSymbols: List[reflect.runtime.universe.Symbol] = List(class MyClass1, class MyClass2)
Run Code Online (Sandbox Code Playgroud)
这是我的设置:
我有一个名为的包foo,其中定义了这些包:
trait FooTrait
case class Bar() extends FooTrait
case class Bar() extends FooTrait
Run Code Online (Sandbox Code Playgroud)
这是我的宏,它获取foo下扩展的类的所有类型符号FooTrait:
def allTypeSymbols_impl[T: c.WeakTypeTag](c: Context)(packageName: c.Expr[String]) = {
import c.universe._
// Get package name from the expression tree
val Literal(Constant(name: String)) = packageName.tree
// …Run Code Online (Sandbox Code Playgroud) 这是我上一个问题的后续行动.
我想像下面的代码一样工作.我希望能够生成一个宏生成的方法:
case class Cat()
test[Cat].method(1)
Run Code Online (Sandbox Code Playgroud)
生成方法本身的实现使用宏("吸血鬼"方法):
// macro call
def test[T] = macro testImpl[T]
// macro implementation
def testImpl[T : c.WeakTypeTag](c: Context): c.Expr[Any] = {
import c.universe._
val className = newTypeName("Test")
// IS IT POSSIBLE TO CALL `otherMethod` HERE?
val bodyInstance = q"(p: Int) => otherMethod(p * 2)"
c.Expr { q"""
class $className {
protected val aValue = 1
@body($bodyInstance)
def method(p: Int): Int = macro methodImpl[Int]
def otherMethod(p: Int): Int = p
}
new …Run Code Online (Sandbox Code Playgroud) 我想回答这个问题.
而不是写:
case class Person(name: String, age: Int) {
def this() = this("",1)
}
Run Code Online (Sandbox Code Playgroud)
我以为我会使用宏注释来扩展它:
@Annotation
case class Person(name: String, age: Int)
Run Code Online (Sandbox Code Playgroud)
所以我尝试DefDef在宏注释的impl中使用quasiquotes 将新构造函数添加为普通的,如:
val newCtor = q"""def this() = this("", 1)"""
val newBody = body :+ newCtor
q"$mods class $name[..$tparams](..$first)(...$rest) extends ..$parents { $self => ..$newBody }"
Run Code Online (Sandbox Code Playgroud)
但是这会返回一个错误: called constructor's definition must precede calling constructor's definition
有办法解决这个问题吗?我错过了什么?
谢谢你看看,朱利安
我有以下代码:
object Macros {
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def hello(): Unit = macro hello_impl
def hello_impl(c: blackbox.Context)(): c.Expr[Unit] = {
import c.universe._
reify {
println("Hello World!")
}
}
}
object Main {
def main(args: Array[String]): Unit = {
Macros.hello()
}
}
Run Code Online (Sandbox Code Playgroud)
它抛出以下编译错误:
Error:(21, 17) macro implementation not found: hello
(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)
Macros.hello()
^
Run Code Online (Sandbox Code Playgroud)
我的问题是:有没有办法"欺骗"编译器,以便在定义它们的同一个文件中使用宏扩展?我的动机如下:我喜欢用Scala编写代码,最近我在网上判断Codeforces中提交了一些问题,而且一些Scala结构变得很慢.所以,我想创建一些宏扩展,以便快速执行这些构造.但我不能提交多个文件.
谢谢!
让我们假设我们有一个密封的特征和一些继承它的案例类:
sealed trait SomeTrait
final case class ClassA(somevalue : Int) extends SomeTrait
final case class ClassB(str : String) extends SomeTrait
(...)
Run Code Online (Sandbox Code Playgroud)
现在我想让所有这些案例类将特征扩展为集合.我将如何继续这样做?我甚至需要在什么类型的类中引用这些类?weakTypeTag?别的什么?
从一般意义上讲,这类似于Travis Brown在这里对case对象做同样的回答.
我的应用程序上下文:我有一个HTTP服务器和一个继承单个密封特征的case类的给定文件(更确切地说:这个层次结构实现了命令设计模式).现在,我想为每个案例类的HTTP-POST自动创建一个端点,并将传入的数据解析upickle到与该端点对应的案例类.以编程方式我不需要其他任何类/类型.
以下是三年多前的Macros: The Plan for Scala 3中的一段话:
例如,我们可以定义一个宏注释 @json,将 JSON 序列化器添加到一种类型中。
知道这在 Scala 3 中如何/是否可行吗?
更一般地说,Scala 3 中有什么可以提供“宏注释”功能吗?以下是宏注释 - Scala 2.13的引用:
与以前版本的宏天堂不同,2.0 中的宏注释在以下意义上是正确的:1) 不仅适用于类和对象,而且适用于任意定义,2)允许扩展类来修改甚至创建伴生对象
scala ×10
scala-macros ×10
macros ×4
case-class ×2
reflection ×2
class ×1
constructor ×1
dotty ×1
import ×1
scala-2.11 ×1
scala-3 ×1