Shapeless 定义 implicit instance 类型的 Witness.Aux[T]
implicit def apply[T]: Witness.Aux[T] = macro SingletonTypeMacros.materializeImpl[T]
和implicit conversion 从类型T 到Witness.Lt[T]
implicit def apply[T](t: T): Witness.Lt[T] = macro SingletonTypeMacros.convertImpl
隐式实例 Witness.Aux[T] 仅基于类型 T 解析(无论是 T is a singleton type or nor),就像普通类型类的隐式实例一样。但是隐式转换T => Witness.Lt[T] 不像普通的隐式转换。普通的隐式转换是根据要转换的值的类型来解决或不解决的。但是T => Witness.Lt[T] 的解析与否不仅取决于T 的类型,还取决于t 本身的值(无论是t is constant/stable or not)。
如果你打开scalacOptions ++= Seq("-Ymacro-debug-lite", "-Xlog-implicits"),你会看到
val w: Witness.Lt[Int] = 3 //compiles
//Warning:scalac: performing macro expansion shapeless.this.Witness.apply[Int](3) at source-/media/data/Projects/macrosdemo213/core/src/main/scala/App114_2.scala,line-9,offset=205
//Warning:scalac: _root_.shapeless.Witness.mkWitness[Int(3)](3.asInstanceOf[Int(3)])
val w2: Witness.Lt[Int] = Random.nextInt(3) //doesn't compile
//Warning:scalac: performing macro expansion shapeless.this.Witness.apply[Int](scala.util.Random.nextInt(3)) at source-/media/data/Projects/macrosdemo213/core/src/main/scala/App114_2.scala,line-10,offset=249
//Warning:scalac: macro expansion has failed: Expression scala.util.Random.nextInt(3) does not evaluate to a constant or a stable reference value
//Error: Expression scala.util.Random.nextInt(3) does not evaluate to a constant or a stable reference value
只检查了implicit def apply[T](t: T): Witness.Lt[T](在w 中工作,但在w2 中没有工作)。
也在
val v1: MayHaveWitness = 3 // compiles but gives None
//Warning:scalac: macro expansion is delayed: shapeless.this.Witness.apply[T]
//Warning:scalac: performing macro expansion shapeless.this.Witness.apply[T]
//Warning:scalac: macro expansion has failed: Type argument T is not a singleton type
//Information: shapeless.this.Witness.apply is not a valid implicit value for Int => shapeless.Witness.Lt[Int] because:
//hasMatchingSymbol reported error: polymorphic expression cannot be instantiated to expected type;
// found : [T]shapeless.Witness.Aux[T]
// (which expands to) [T]shapeless.Witness{type T = T}
// required: Int => shapeless.Witness.Lt[Int]
// (which expands to) Int => shapeless.Witness{type T <: Int}
//Information: App.this.MayHaveWitness.fromLit is not a valid implicit value for Int(3) => App.MayHaveWitness because:
//No implicit view available from Int => shapeless.Witness.Lt[Int].
在
val v2: MayHaveWitness = Random.nextInt(3) // compiles but gives None
//Warning:scalac: macro expansion is delayed: shapeless.this.Witness.apply[T]
//Warning:scalac: performing macro expansion shapeless.this.Witness.apply[T]
//Warning:scalac: macro expansion has failed: Type argument T is not a singleton type
//Warning:scalac: performing macro expansion shapeless.this.Witness.apply[T]
//Information: App.this.MayHaveWitness.fromLit is not a valid implicit value for Int => App.MayHaveWitness because:
//No implicit view available from Int => shapeless.Witness.Lt[Int].
//Information: shapeless.this.Witness.apply is not a valid implicit value for Int => shapeless.Witness.Lt[Int] because:
//hasMatchingSymbol reported error: polymorphic expression cannot be instantiated to expected type;
// found : [T]shapeless.Witness.Aux[T]
// (which expands to) [T]shapeless.Witness{type T = T}
// required: Int => shapeless.Witness.Lt[Int]
// (which expands to) Int => shapeless.Witness{type T <: Int}
//Information: App.this.MayHaveWitness.fromLit is not a valid implicit value for Int => App.MayHaveWitness because:
//No implicit view available from Int => shapeless.Witness.Lt[Int].
implicit def apply[T]: Witness.Aux[T] 和 implicit def apply[T](t: T): Witness.Lt[T] 都经过检查,但都没有工作。
为什么implicit proof: T => Witness.Lt[Int]不是以下无形宏的成功召唤师?
编译器处理函数类型A => B 的隐式与其他类型的隐式不同。它可以将它们视为隐式转换(视图)。但它是否实际上将它们视为转换或只是类型为 A => B 的隐式实例(与其他类型一样)取决于布尔值 flag isView。
当你这样做时
val w: Witness.Lt[Int] = 3 //compiles
val w2: Witness.Lt[Int] = Random.nextInt(3) //doesn't compile
val v1: MayHaveWitness = 3 //compiles
val v2: MayHaveWitness = Random.nextInt(3) //compiles
isView 是 true。但是当你这样做时
implicitly[Int => Witness.Lt[Int]] //doesn't compile
implicitly[3 => Witness.Lt[Int]] //doesn't compile
implicitly[Int => MayHaveWitness] //doesn't compile
implicitly[3 => MayHaveWitness] //doesn't compile
或这里
implicit def fromLit... (implicit proof: T => Witness.Lt[Int]) ...
______________________________________
isView 是false。
在简单的情况下,隐式 A => B 的存在和从 A 到 B 的隐式转换是相同的
class A
class B
// implicit val aToB: A => B = null // this one
implicit def aToB(a: A): B = null // or this one
implicitly[A => B] //compiles
val b: B = new A //compiles
但在我们的例子中不是。有隐式转换3 => Witness.Lt[3] 但没有这种类型的实例
val w: Witness.Lt[3] = 3.asInstanceOf[3] //compiles
implicitly[3 => Witness.Lt[3]] // doesn't compile
//Information: shapeless.this.Witness.apply is not a valid implicit value for 3 => shapeless.Witness.Lt[3] because:
//hasMatchingSymbol reported error: polymorphic expression cannot be instantiated to expected type;
// found : [T]shapeless.Witness.Aux[T]
// (which expands to) [T]shapeless.Witness{type T = T}
// required: 3 => shapeless.Witness.Lt[3]
// (which expands to) 3 => shapeless.Witness{type T <: 3}
//Error: No implicit view available from 3 => shapeless.Witness.Lt[3].
所以它检查implicit def apply[T]: Witness.Aux[T],但不检查implicit def apply[T](t: T): Witness.Lt[T]。我没有深入调试隐式解析,但我怀疑在解析隐式之前没有推断出某些类型。
没有标准方法可以打开isView,以便在解析... def fromLit... (implicit proof: T => Witness.Lt[Int]) ... 中的proof 时完全模拟隐式转换的行为。如果我们使用c.inferImplicitView 而不是c.inferImplicitValue,我们可以使用宏打开isView
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
trait ImplicitView[A, B] {
def instance: A => B
}
object ImplicitView {
implicit def mkImplicitView[A, B]: ImplicitView[A, B] = macro mkImplicitViewImpl[A, B]
def mkImplicitViewImpl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val tpA = weakTypeOf[A]
val tpB = weakTypeOf[B]
val x = TermName(c.freshName("x"))
val conversion = c.inferImplicitView(tree = EmptyTree, from = tpA, to = tpB, silent = false)
q"""new ImplicitView[$tpA, $tpB] {
def instance: $tpA => $tpB = ($x: $tpA) => $conversion($x)
}"""
}
让我们替换
implicit def fromLit[T](literal: T)(implicit proof: T => Witness.Lt[Int]): MayHaveWitness.Some = new Some(literal)
与
implicit def fromLit[T](literal: T)(implicit proof: ImplicitView[T, Witness.Lt[Int]]): MayHaveWitness.Some = new Some(proof.instance(literal))
我们还要修改
implicit def fromNonLit(v: Int): None.type = None
因为它与 fromLit 不明确。原因类似于those。最简单的解决方法是将其替换为
implicit def fromNonLit[T](v: T): None.type = None
现在两个
val v1: MayHaveWitness = 3
println(v1.getClass)
val v2: MayHaveWitness = Random.nextInt(3)
println(v2.getClass)
给Some(我怀疑这不是你想要的)。这是可以理解的。 Random.nextInt(3) 是 Int。我们只根据类型解析MayHaveWitness。并且有隐式转换Int => Witness.Lt[Int]。所以是Some。
因此,如果我们希望v1 提供Some 和v2 提供None,那么我们不能仅基于类型来做到这一点。所以使用类型类的方法是行不通的,我们必须使用宏。
trait MayHaveWitness {
type Lit
}
object MayHaveWitness {
class Some(val w: Witness.Lt[Int]) extends MayHaveWitness {
type Lit = w.T
}
object None extends MayHaveWitness {
type Lit = Nothing
}
implicit def fromLit[T](literal: T): MayHaveWitness = macro fromLitImpl[T]
def fromLitImpl[T: c.WeakTypeTag](c: whitebox.Context)(literal: c.Tree): c.Tree = {
import c.universe._
val conversion = c.inferImplicitView(tree = literal, from = weakTypeOf[T], to = typeOf[Witness.Lt[Int]], silent = false)
util.Try(c.typecheck(q"new MayHaveWitness.Some($conversion($literal))"))
.getOrElse(q"MayHaveWitness.None")
}
}
在这里,我们将(implicit proof: T => Witness.Lt[Int]) 替换为c.inferImplicitView...,我们不仅探索了literal 的类型,还探索了literal 本身。
现在
val v1: MayHaveWitness = 3
println(v1.getClass)
val v2: MayHaveWitness = Random.nextInt(3)
println(v2.getClass)
v1 给Some 和v2 给None。
如果您制作fromLit blackbox,它仍然可以工作,但会返回MayHaveWitness 而不是MayHaveWitness.Some 和MayHaveWitness.None。