隐式Mapper对隐式Mapped参数的奇怪影响
Strange influence of implicit Mapper on implicit Mapped parameter
假设我有容器标记
case class TypedString[T](value: String)
和偏函数技巧
abstract class PartFunc[Actual <: HList] {
val poly: Poly
def apply[L1 <: HList](l: L1)(implicit
mapped: Mapped.Aux[Actual, TypedString, L1],
mapper: Mapper[poly.type, L1]): L1 = l
}
映射器的多边形
object f extends (TypedString ~>> String) {
def apply[T](s : TypedString[T]) = s.value
}
和结果方法
def func[Actual <: HList] = new PartFunc[Actual] {
val poly = f
}
用法示例:
func[
Int :: String :: HNil
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)
此代码在编译时失败,因为编译器找不到 Mapped
隐式参数:
could not find implicit value for parameter mapped:
shapeless.ops.hlist.Mapped[shapeless.::[Int,shapeless.::[String,shapeless.HNil]],nottogether.MapperTest.TypedString]{type Out = shapeless.::[nottogether.MapperTest.TypedString[Int],shapeless.::[nottogether.MapperTest.TypedString[String],shapeless.HNil]]}
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)
但是,如果我们从 PartFunc.apply(...)
签名中删除 Mapper
隐式参数,一切正常。所以我不知道 Mapper
为什么以及如何影响 Mapped
。
编译器抱怨 Mapped
,而实际问题出在 Mapper
。我不确定为什么,但是当 poly
是抽象值或 [=18= 的构造函数参数时,为单例类型 poly.type
获取 Mapped
似乎出了点问题].
一个解决方案是使 poly
成为 P <: Poly
并在我们创建 PartFunc
时将单例类型与 Actual
一起传递:
import shapeless._
import ops.hlist.{Mapped, Mapper}
import poly.~>>
abstract class PartFunc[Actual <: HList, P <: Poly] {
val poly: P
def apply[L1 <: HList](l: L1)(implicit
mapped: Mapped.Aux[Actual, TypedString, L1],
mapper: Mapper[P, L1]
): mapper.Out = mapper(l)
}
def func[Actual <: HList] = new PartFunc[Actual, f.type] { val poly = f }
或使用常规 class :
class PartFunc[Actual <: HList, P <: Poly](poly: P) {
def apply[L1 <: HList](l: L1)(implicit
mapped: Mapped.Aux[Actual, TypedString, L1],
mapper: Mapper[P, L1]
): mapper.Out = mapper(l)
}
def func[Actual <: HList] = new PartFunc[Actual, f.type](f)
注意我们现在必须写 mapper(l)
,因为 l map poly
仍然会寻找 Mapped[poly.type, L1]
.
我们现在可以调用 func
:
func[
Int :: String :: HNil
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)
// String :: String :: HNil = 42 :: hello :: HNil
我相信对 Scala 类型系统有更深入了解的人可以为我们提供更清晰的解释,并可能为这个问题提供更好的解决方案。
假设我有容器标记
case class TypedString[T](value: String)
和偏函数技巧
abstract class PartFunc[Actual <: HList] {
val poly: Poly
def apply[L1 <: HList](l: L1)(implicit
mapped: Mapped.Aux[Actual, TypedString, L1],
mapper: Mapper[poly.type, L1]): L1 = l
}
映射器的多边形
object f extends (TypedString ~>> String) {
def apply[T](s : TypedString[T]) = s.value
}
和结果方法
def func[Actual <: HList] = new PartFunc[Actual] {
val poly = f
}
用法示例:
func[
Int :: String :: HNil
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)
此代码在编译时失败,因为编译器找不到 Mapped
隐式参数:
could not find implicit value for parameter mapped:
shapeless.ops.hlist.Mapped[shapeless.::[Int,shapeless.::[String,shapeless.HNil]],nottogether.MapperTest.TypedString]{type Out = shapeless.::[nottogether.MapperTest.TypedString[Int],shapeless.::[nottogether.MapperTest.TypedString[String],shapeless.HNil]]}
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)
但是,如果我们从 PartFunc.apply(...)
签名中删除 Mapper
隐式参数,一切正常。所以我不知道 Mapper
为什么以及如何影响 Mapped
。
编译器抱怨 Mapped
,而实际问题出在 Mapper
。我不确定为什么,但是当 poly
是抽象值或 [=18= 的构造函数参数时,为单例类型 poly.type
获取 Mapped
似乎出了点问题].
一个解决方案是使 poly
成为 P <: Poly
并在我们创建 PartFunc
时将单例类型与 Actual
一起传递:
import shapeless._
import ops.hlist.{Mapped, Mapper}
import poly.~>>
abstract class PartFunc[Actual <: HList, P <: Poly] {
val poly: P
def apply[L1 <: HList](l: L1)(implicit
mapped: Mapped.Aux[Actual, TypedString, L1],
mapper: Mapper[P, L1]
): mapper.Out = mapper(l)
}
def func[Actual <: HList] = new PartFunc[Actual, f.type] { val poly = f }
或使用常规 class :
class PartFunc[Actual <: HList, P <: Poly](poly: P) {
def apply[L1 <: HList](l: L1)(implicit
mapped: Mapped.Aux[Actual, TypedString, L1],
mapper: Mapper[P, L1]
): mapper.Out = mapper(l)
}
def func[Actual <: HList] = new PartFunc[Actual, f.type](f)
注意我们现在必须写 mapper(l)
,因为 l map poly
仍然会寻找 Mapped[poly.type, L1]
.
我们现在可以调用 func
:
func[
Int :: String :: HNil
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)
// String :: String :: HNil = 42 :: hello :: HNil
我相信对 Scala 类型系统有更深入了解的人可以为我们提供更清晰的解释,并可能为这个问题提供更好的解决方案。