感谢上一个问题的答案,我能够创建一个函数宏,使得它返回一个Map将每个字段名称映射到它的类值,例如
...
trait Model
case class User (name: String, age: Int, posts: List[String]) extends Model {
val numPosts: Int = posts.length
...
def foo = "bar"
...
}
Run Code Online (Sandbox Code Playgroud)
所以这个命令
val myUser = User("Foo", 25, List("Lorem", "Ipsum"))
myUser.asMap
Run Code Online (Sandbox Code Playgroud)
回报
Map("name" -> "Foo", "age" -> 25, "posts" -> List("Lorem", "Ipsum"), "numPosts" -> 2)
Run Code Online (Sandbox Code Playgroud)
这就是生成Tuples的Map地方(见Travis Brown的回答):
...
val pairs = weakTypeOf[T].declarations.collect {
case m: MethodSymbol if m.isAccessor =>
val name = c.literal(m.name.decoded)
val …Run Code Online (Sandbox Code Playgroud) 对于我的一个项目,我已经实现了一个基于的枚举
trait Enum[A] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}
sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency
case object GBP extends Currency
}
Run Code Online (Sandbox Code Playgroud)
来自Case对象与Scala中的枚举.我工作得很好,直到遇到以下问题.Case对象似乎很懒惰,如果我使用Currency.value,我可能实际上得到一个空列表.可以在启动时对所有枚举值进行调用,以便填充值列表,但这将有点击败这一点.
因此,我冒险进入scala反射的黑暗和未知的地方,并根据以下SO答案提出了这个解决方案.我是否可以获得所有案例对象的编译时列表,这些案例对象派生自Scala中的密封父代? 和我怎样才能获得通过斯卡拉2.10反射提到了实际的对象?
import scala.reflect.runtime.universe._
abstract class Enum[A: TypeTag] {
trait Value
private def sealedDescendants: Option[Set[Symbol]] = {
val symbol = typeOf[A].typeSymbol
val internal = symbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol]
if (internal.isSealed)
Some(internal.sealedDescendants.map(_.asInstanceOf[Symbol]) - symbol) …Run Code Online (Sandbox Code Playgroud) 在scala宏上执行测试的建议方法是什么?
我意识到由于需要单独编译,需要两个项目.如有必要,此步骤可以接受并且大部分都是清楚的.
但是,如何断言宏扩展失败呢?如果没有一些特殊功能,测试用例将无法编译,因此整个测试项目将无法编译.
我认为这个断言需要另一个形式的宏
errors(code: => _): List[CompileError]
Run Code Online (Sandbox Code Playgroud)
它返回内部宏的编译错误.如果他们应该发出警告,那么测试警告也是必需的......等等......
Scala宏是否有一些现有的测试工具?
我正在尝试实现一个StringContext扩展,这将允许我写这个:
val tz = zone"Europe/London" //tz is of type java.util.TimeZone
Run Code Online (Sandbox Code Playgroud)
但有一点需要注意,如果提供的时区无效(假设可以在编译时确定),它将无法编译.
这是一个辅助函数:
def maybeTZ(s: String): Option[java.util.TimeZone] =
java.util.TimeZone.getAvailableIDs collectFirst { case id if id == s =>
java.util.TimeZone.getTimeZone(id)
}
Run Code Online (Sandbox Code Playgroud)
我可以很容易地创建一个非宏实现:
scala> implicit class TZContext(val sc: StringContext) extends AnyVal {
| def zone(args: Any *) = {
| val s = sc.raw(args.toSeq : _ *)
| maybeTZ(s) getOrElse sys.error(s"Invalid zone: $s")
| }
| }
Run Code Online (Sandbox Code Playgroud)
然后:
scala> zone"UTC"
res1: java.util.TimeZone = sun.util.calendar.ZoneInfo[id="UTC",offset=0,...
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.如果时区是无意义的(例如zone"foobar"),那么编译不会失败; 代码在运行时失效.我想将它扩展到一个宏,但是,尽管阅读了文档 …
我定义了以下宏来将case字段转换为map
import scala.language.experimental.macros
import scala.reflect.macros.Context
def asMap_impl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]) = {
import c.universe._
val mapApply = Select(reify(Map).tree, newTermName("apply"))
val pairs = weakTypeOf[T].declarations.collect {
case m: MethodSymbol if m.isCaseAccessor =>
val name = c.literal(m.name.decoded)
val value = c.Expr(Select(t.tree, m.name))
reify(name.splice -> value.splice).tree
}
c.Expr[Map[String, Any]](Apply(mapApply, pairs.toList))
}
Run Code Online (Sandbox Code Playgroud)
并且方法实现
def asMap[T](t: T) = macro asMap_impl[T]
Run Code Online (Sandbox Code Playgroud)
然后我定义一个案例类来测试它
case class User(name : String)
Run Code Online (Sandbox Code Playgroud)
它工作正常(使用scala repl):
scala> asMap(User("foo")) res0:
scala.collection.immutable.Map[String,String] = Map(name -> foo)
Run Code Online (Sandbox Code Playgroud)
但是当我用另一个泛型方法包装此方法时
def printlnMap[T](t: T) = println(asMap(t))
Run Code Online (Sandbox Code Playgroud)
此方法始终打印空地图:
scala> printlnMap(User("foo"))
Map() …Run Code Online (Sandbox Code Playgroud) 这个限制将来会被克服吗?或者有什么办法解决这个我不知道的事情?
现在我有一个项目,我想使用宏,但他们需要依赖于该项目的特定类型.有一个单独的宏编译单元需要我引入另一个来保存常见类型,我不愿意每次写宏时都这样做.
如何将参数/设置传递给Scala宏?
这些设置不应该是全局的,而是每次宏调用.
我想要的是类似的东西:
def a(param: Int) = macro internalMacro("setting 1")
def b(param: Int) = macro internalMacro("setting 2")
Run Code Online (Sandbox Code Playgroud)
而setting 1和setting 2应然后是恒定值,从宏观中访问,这样我就可以使内部行为依赖于他们.
考虑io包装中提供的默认编解码器。
implicitly[io.Codec].name //res0: String = UTF-8
Run Code Online (Sandbox Code Playgroud)
它是“低优先级”隐式的,因此很容易覆盖而不会产生歧义。
implicit val betterCodec: io.Codec = io.Codec("US-ASCII")
implicitly[io.Codec].name //res1: String = US-ASCII
Run Code Online (Sandbox Code Playgroud)
提高优先级也很容易。
import io.Codec.fallbackSystemCodec
implicit val betterCodec: io.Codec = io.Codec("US-ASCII")
implicitly[io.Codec].name //won't compile: ambiguous implicit values
Run Code Online (Sandbox Code Playgroud)
但是我们可以朝相反的方向前进吗?我们可以创建一个低级别的隐式来禁用(“歧义化”)默认值吗?我一直在研究优先级方程式,并使用低优先级隐式变量进行操作,但尚未创建默认值的歧义。
我正在尝试在 Scala 中实现康威的超现实数字。超现实数是递归定义的——作为一对超现实数的集合,称为左和右,这样右集中的任何元素都不小于或等于左集中的任何元素。这里超现实数之间的关系“小于或等于”也是递归定义的:我们说x ? Ÿ如果
我们首先将零定义为一对空集,然后使用零来定义 1 和 -1,依此类推。
我无法弄清楚如何在编译时强制执行超现实数字的定义。这就是我现在所拥有的:
case class SurrealNumber(left: Set[SurrealNumber], right: Set[SurrealNumber]) {
if ((for { a <- left; b <- right; if b <= a } yield (a, b)).nonEmpty)
throw new Exception
def <=(other: SurrealNumber): Boolean =
!this.left.exists(other <= _) && !other.right.exists(_ <= this)
}
val zero = SurrealNumber(Set.empty, Set.empty)
val one = …Run Code Online (Sandbox Code Playgroud) 我正在尝试编写一个宏,它接受一个带有java bean接口和case类的类,并创建一对用于在它们之间进行映射的方法.
我试图检查每个属性的类型是否匹配,但java bean中的类型是例如java.lang.Long,case类中的类型是scala.Long.
我的问题是,给定这些2的c.universe.Type对象,有没有办法测试它们之间是否存在隐式转换?即测试我是否可以将一个传递给一个期望另一个的方法.
scala ×10
scala-macros ×10
annotations ×1
compilation ×1
enums ×1
generics ×1
implicit ×1
jvm ×1
macros ×1
scala-2.10 ×1
shapeless ×1
types ×1