Skip to content

Instantly share code, notes, and snippets.

@matsu-chara
Last active August 29, 2015 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save matsu-chara/de8b85b6bf21f5fa977c to your computer and use it in GitHub Desktop.
Save matsu-chara/de8b85b6bf21f5fa977c to your computer and use it in GitHub Desktop.
dynamic版 ?演算子 (なぜかimplicit conversionされないのと、dynamicでフィールド名を可変にしても {def value: B}という構造的部分型の部分が可変にならないので詰んだ・・・(◞‸◟)
name := "OptionValueAccessor"
version := "1.0"
scalaVersion := "2.11.6"
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value
scalacOptions += "-language:dynamics"
package OptionValueAccessor
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
object OptionValueAccessor {
implicit class OptValue[A, B](val self: Option[A])(implicit ev: A <:< {def value: B}) extends Dynamic {
def selectDynamic(name: String): Option[B] = macro Impl.Opt.selectDynamicImpl[B]
}
implicit class OptOpt[A, B](val self: Option[Option[A]])(implicit ev: A <:< {def value: B}) {
def selectDynamic(name: String): Option[B] = macro Impl.OptOpt.selectDynamicImpl[B]
}
object Impl {
object Opt {
def selectDynamicImpl[B: c.WeakTypeTag](c: whitebox.Context)(name: c.Expr[String]): c.Expr[Option[B]] = {
import c.universe._
val nameStr: String = name.tree match {
case pq"${n: String}" if n.startsWith("_") => n.drop(1)
case _ => c.abort(c.enclosingPosition, s"#$name not found.")
}
c.Expr[Option[B]](q"${c.prefix}.self.map {_.${TermName(nameStr)}}")
}
}
object OptOpt {
def selectDynamicImpl[B: c.WeakTypeTag](c: whitebox.Context)(name: c.Expr[String]): c.Expr[Option[B]] = {
import c.universe._
val nameStr: String = name.tree match {
case pq"${n: String}" if n.startsWith("_") => n.drop(1)
case _ => c.abort(c.enclosingPosition, s"#$name not found.")
}
c.Expr[Option[B]](q"${c.prefix}.self.flatten.map {_.${TermName(nameStr)}}")
}
}
}
}
package OptionValueAccessor
import OptionValueAccessor._
object OptionValueAccessorTest {
case class A(value: Int)
case class B(value: Option[A])
case class C(value: Option[B])
case class D(value: Option[C])
case class E(value: D)
def main(args: Array[String]): Unit = {
val edcba: Option[E] = Some(E(D(Some(C(Some(B(Some(A(1)))))))))
println(
edcba
.selectDynamic("_value")
.selectDynamic("_value")
.selectDynamic("_value")
.selectDynamic("_value")
.selectDynamic("_value")
)
val edcbnone = Some(E(D(None)))
println(
edcbnone
.selectDynamic("_value")
.selectDynamic("_value")
.selectDynamic("_value")
.selectDynamic("_value")
.selectDynamic("_value")
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment