加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 使用宏构建列表时的Infer HList类型

发布时间:2020-12-16 18:58:18 所属栏目:安全 来源:网络整理
导读:我有一个方法采用HList并使用它来构建类的实例. 我想提供一些简化的语法,隐藏明确的缺点.所以我想从: MyThingy.describe( 42 :: true :: "string" :: HNil) 至 MyThingy.describe { 42 true "string"} 其中MyThingy定义为 class MyThingy[L : HList](elems:
我有一个方法采用HList并使用它来构建类的实例.
我想提供一些简化的语法,隐藏明确的缺点.所以我想从:

MyThingy.describe( 42 :: true :: "string" :: HNil)

MyThingy.describe {
  42
  true
  "string"
}

其中MyThingy定义为

class MyThingy[L <: HList](elems: L)

我试过这个宏

def describe[L <: HList](elements: Unit): MyThingy[L] = macro MyThingyMacros.describeImpl[L]

def describeImpl[L <: shapeless.HList : c.WeakTypeTag](c: Context)(elems: c.Tree): c.Tree = {
  import c.universe._

  def concatHList: PartialFunction[Tree,Tree] = {
    case Block(l,_) =>
      val els = l.reduceRight((x,y) => q"shapeless.::($x,$y)")
      q"$els :: shapeless.HNil"
  }

  concatHList.lift(elems) match {
    case None => c.abort(c.enclosingPosition,"BOOM!")
    case Some(elemsHList) =>
      val tpe = c.typecheck(elemsHList).tpe
      q"new MyThingy[$tpe]($elemsHList)"
  }

}

但是typechecker爆炸了:

exception during macro expansion:
scala.reflect.macros.TypecheckException: inferred type arguments [Int,Boolean] do not conform to method apply’s type parameter bounds [H,T <: shapeless.HList]

显然,编译器试图在宏扩展之前从块中推断出[Int,Boolean].我也不明白为什么它需要两个参数,其中describe和MyThing只需要一个参数.

有没有办法让由宏生成的树驱动类型推断?

解决方法

我会在这里恭敬地不同意迈尔斯.我个人无法忍受自动翻译,如果你想在你的项目中使用-Xlint,他的答案中的解决方案会引起很多警告噪音.我绝对同意你应该避免宏,当有一个可行的选择,但如果我必须在自动元组和宏之间做出选择,我只是提供语法糖,我会选择宏.

在你的情况下,这不是太难 – 你的逻辑中只有一个小错误(好吧,两个,真的).以下将工作得很好:

import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
import shapeless._

class MyThingy[L <: HList](val elems: L)

def describeImpl[L <: HList: c.WeakTypeTag](c: Context)(elems: c.Tree) = {
  import c.universe._

  def concatHList: PartialFunction[Tree,Tree] = {
    case Block(statements,last) =>
      statements.foldRight(q"$last :: shapeless.HNil")(
        (h,t) => q"shapeless.::($h,$t)"
      )
  }

  concatHList.lift(elems) match {
    case None => c.abort(c.enclosingPosition,"BOOM!")
    case Some(elemsHList) =>
      val tpe = c.typecheck(elemsHList).tpe
      q"new MyThingy[$tpe]($elemsHList)"
  }
}

def describe[L <: HList](elems: Any): MyThingy[L] = macro describeImpl[L]

或者更简洁:

def describeImpl[L <: HList: c.WeakTypeTag](c: Context)(elems: c.Tree) = {
  import c.universe._

  elems match {
    case q"{ ..$elems }" =>
      val hlist = elems.foldRight[c.Tree](q"shapeless.HNil: shapeless.HNil")(
        (h,$t)"
      )
      q"new MyThingy($hlist)"
    case _ => c.abort(c.enclosingPosition,"BOOM!")
  }
}

最大的问题只在于减少 – 你需要从HNil开始,而不是建立一个毫无意义的中间事物,然后再加以解决.您还需要捕获块的表达式,并将其键入为Any而不是Unit,以避免丢弃值.

(作为旁注,我有点惊讶它作为白盒宏工作,但从2.11.2开始.)

我个人更喜欢这种语法和逗号,这也很简单:

def describeImpl[L <: HList: c.WeakTypeTag](c: Context)(elems: c.Tree*) = {
  import c.universe._

  val hlist = elems.foldRight[c.Tree](q"shapeless.HNil: shapeless.HNil")(
    (h,$t)"
  )

  q"new MyThingy($hlist)"
}

def describe[L <: HList](elems: Any*): MyThingy[L] = macro describeImpl[L]

此处的用法与产品解决方案相同,但不涉及自动翻译.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读