Shapeless 在测试中找不到隐式,但可以在 REPL 中找到
Shapeless not finding implicits in test, but can in REPL
我有一个案例 class 看起来像这样:
case class Color(name: String, red: Int, green: Int, blue: Int)
我正在使用 Shapeless 2.3.1 和 Scala 2.11.8。在查找 LabelledGeneric[Color]
的隐式值方面,我从我的测试和 REPL 中看到了不同的行为。 (实际上我正在尝试自动派生一些其他类型 class,但我也得到 null
)
内测
package foo
import shapeless._
import org.specs2.mutable._
case class Color(name: String, red: Int, green: Int, blue: Int)
object CustomProtocol {
implicit val colorLabel: LabelledGeneric[Color] = LabelledGeneric[Color]
}
class GenericFormatsSpec extends Specification {
val color = Color("CadetBlue", 95, 158, 160)
"The case class example" should {
"behave as expected" in {
import CustomProtocol._
assert(colorLabel != null, "colorLabel is null")
1 mustEqual 1
}
}
}
此测试失败,因为 colorLabel
是 null
。为什么?
REPL
从 REPL 中,我可以找到 LabelledGeneric[Color]
:
scala> case class Color(name: String, red: Int, green: Int, blue: Int)
defined class Color
scala> import shapeless._
import shapeless._
scala> LabelledGeneric[Color]
res0: shapeless.LabelledGeneric[Color]{type Repr = shapeless.::[String with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("name")],String],shapeless.::[Int with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("red")],Int],shapeless.::[Int with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("green")],Int],shapeless.::[Int with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("blue")],Int],shapeless.HNil]]]]} = shapeless.LabelledGeneric$$anon@755f11d9
我刚刚意识到我正在为隐式输入 return 类型:
object CustomProtocol {
implicit val colorLabel: LabelledGeneric[Color] = LabelledGeneric[Color]
}
但 REPL 中的实际 return 类型类似于
shapeless.LabelledGeneric[Color]{type Repr = shapeless.::[String with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("name")],String],shapeless.::[Int with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("red")],Int],shapeless.::[Int with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("green")],Int],shapeless.::[Int with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("blue")],Int],shapeless.HNil]]]]}
删除类型注释后测试通过:
object CustomProtocol {
implicit val colorLabel = LabelledGeneric[Color]
}
这令人惊讶,因为通常我们被鼓励为隐式添加类型注释。
您所看到的 null
确实是带有和不带有显式注释类型的隐式定义语义的令人惊讶的结果。定义右侧的表达式 LabelledGeneric[Color]
是在 object LabelledGeneric
上使用类型参数 Color
调用 apply
方法,它本身需要类型为隐式参数LabelledGeneric[Color]
。隐式查找规则意味着具有最高优先级的相应范围内隐式定义是当前正在定义的 implicit val colorLabel
,即。我们有一个循环,它以获取默认 null
初始值设定项的值结束。如果,OTOH,类型注释被关闭, colorLabel
不在范围内,您将得到您期望的结果。这很不幸,因为正如您正确观察到的那样,我们应该尽可能明确地注释隐式定义。
shapeless 的 cachedImplicit
提供了解决这个问题的机制,但在描述它之前我需要指出一个额外的复杂性。类型 LabelledGeneric[Color]
不是 colorLabel
的正确类型。 LabelledGeneric
有一个类型成员 Repr
,它是您要为其实例化 LabelledGeneric
的类型的表示类型,并且通过注释您所拥有的定义,您明确地放弃了 LabelledGeneric[Color]
其中包括那个。结果值将毫无用处,因为它的类型不够精确。用正确的类型注释隐式定义,无论是显式细化还是使用等价物 Aux
都很困难,因为表示类型很难显式写出,
object CustomProtocol {
implicit val colorLabel: LabelledGeneric.Aux[Color, ???] = ...
}
同时解决这两个问题需要两个步骤,
- 获取
LabelledGeneric
具有完全优化类型的实例。
- 使用显式注释定义缓存的隐式值,但不生成导致
null
. 的初始化循环
最终看起来像这样,
object CustomProtocol {
val gen0 = cachedImplicit[LabelledGeneric[Color]]
implicit val colorLabel: LabelledGeneric.Aux[Color, gen0.Repr] = gen0
}
我有一个案例 class 看起来像这样:
case class Color(name: String, red: Int, green: Int, blue: Int)
我正在使用 Shapeless 2.3.1 和 Scala 2.11.8。在查找 LabelledGeneric[Color]
的隐式值方面,我从我的测试和 REPL 中看到了不同的行为。 (实际上我正在尝试自动派生一些其他类型 class,但我也得到 null
)
内测
package foo
import shapeless._
import org.specs2.mutable._
case class Color(name: String, red: Int, green: Int, blue: Int)
object CustomProtocol {
implicit val colorLabel: LabelledGeneric[Color] = LabelledGeneric[Color]
}
class GenericFormatsSpec extends Specification {
val color = Color("CadetBlue", 95, 158, 160)
"The case class example" should {
"behave as expected" in {
import CustomProtocol._
assert(colorLabel != null, "colorLabel is null")
1 mustEqual 1
}
}
}
此测试失败,因为 colorLabel
是 null
。为什么?
REPL
从 REPL 中,我可以找到 LabelledGeneric[Color]
:
scala> case class Color(name: String, red: Int, green: Int, blue: Int)
defined class Color
scala> import shapeless._
import shapeless._
scala> LabelledGeneric[Color]
res0: shapeless.LabelledGeneric[Color]{type Repr = shapeless.::[String with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("name")],String],shapeless.::[Int with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("red")],Int],shapeless.::[Int with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("green")],Int],shapeless.::[Int with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("blue")],Int],shapeless.HNil]]]]} = shapeless.LabelledGeneric$$anon@755f11d9
我刚刚意识到我正在为隐式输入 return 类型:
object CustomProtocol {
implicit val colorLabel: LabelledGeneric[Color] = LabelledGeneric[Color]
}
但 REPL 中的实际 return 类型类似于
shapeless.LabelledGeneric[Color]{type Repr = shapeless.::[String with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("name")],String],shapeless.::[Int with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("red")],Int],shapeless.::[Int with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("green")],Int],shapeless.::[Int with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("blue")],Int],shapeless.HNil]]]]}
删除类型注释后测试通过:
object CustomProtocol {
implicit val colorLabel = LabelledGeneric[Color]
}
这令人惊讶,因为通常我们被鼓励为隐式添加类型注释。
您所看到的 null
确实是带有和不带有显式注释类型的隐式定义语义的令人惊讶的结果。定义右侧的表达式 LabelledGeneric[Color]
是在 object LabelledGeneric
上使用类型参数 Color
调用 apply
方法,它本身需要类型为隐式参数LabelledGeneric[Color]
。隐式查找规则意味着具有最高优先级的相应范围内隐式定义是当前正在定义的 implicit val colorLabel
,即。我们有一个循环,它以获取默认 null
初始值设定项的值结束。如果,OTOH,类型注释被关闭, colorLabel
不在范围内,您将得到您期望的结果。这很不幸,因为正如您正确观察到的那样,我们应该尽可能明确地注释隐式定义。
shapeless 的 cachedImplicit
提供了解决这个问题的机制,但在描述它之前我需要指出一个额外的复杂性。类型 LabelledGeneric[Color]
不是 colorLabel
的正确类型。 LabelledGeneric
有一个类型成员 Repr
,它是您要为其实例化 LabelledGeneric
的类型的表示类型,并且通过注释您所拥有的定义,您明确地放弃了 LabelledGeneric[Color]
其中包括那个。结果值将毫无用处,因为它的类型不够精确。用正确的类型注释隐式定义,无论是显式细化还是使用等价物 Aux
都很困难,因为表示类型很难显式写出,
object CustomProtocol {
implicit val colorLabel: LabelledGeneric.Aux[Color, ???] = ...
}
同时解决这两个问题需要两个步骤,
- 获取
LabelledGeneric
具有完全优化类型的实例。 - 使用显式注释定义缓存的隐式值,但不生成导致
null
. 的初始化循环
最终看起来像这样,
object CustomProtocol {
val gen0 = cachedImplicit[LabelledGeneric[Color]]
implicit val colorLabel: LabelledGeneric.Aux[Color, gen0.Repr] = gen0
}