scala - 在 scala 中,如何使类型类适用于 Aux 模式?- 第2部分
问题描述
这是一个后续问题:在 scala 中,如何使类型类适用于 Aux 模式?
考虑以下示例:
trait Base {
type Out
def v: Out
}
object Base {
type Aux[T] = Base { type Out = T }
type Lt[T] = Base { type Out <: T }
class ForH() extends Base {
final type Out = HNil
override def v: Out = HNil
}
object ForH extends ForH
}
trait TypeClasses {
class TypeClass[B]
def summon[B](b: B)(implicit ev: TypeClass[B]): TypeClass[B] = ev
}
object T1 extends TypeClasses {
implicit def t1: TypeClass[Base.Aux[HNil]] = new TypeClass[Base.Aux[HNil]]
implicit def t2: TypeClass[Int] = new TypeClass[Int]
}
object T2 extends TypeClasses {
implicit def t1[T <: Base.Aux[HNil]]: TypeClass[T] = new TypeClass[T]
}
object T3 extends TypeClasses {
implicit def t1[
H <: HList,
T <: Base.Lt[H]
]: TypeClass[T] = new TypeClass[T] {
type HH = H
}
}
object T4 extends TypeClasses {
implicit def t1[
H <: HList,
T <: Base.Aux[H]
]: TypeClass[T] = new TypeClass[T] {
type HH = H
}
}
it("No Aux") {
val v = 2
T1.summon(v) // works
}
it("Aux1") {
val v = new Base.ForH()
T1.summon(v) // oops
T1.summon(Base.ForH) // oops
val v2 = new Base.ForH(): Base.Aux[HNil]
T1.summon(v2) // works!
}
it("Aux2") {
val v = new Base.ForH()
T2.summon(v) // works
T2.summon(Base.ForH) // works
val v2 = new Base.ForH(): Base.Aux[HNil]
T2.summon(v2) // works
}
it("Aux3") {
val v = new Base.ForH()
T3.summon(v) // oops
T3.summon(Base.ForH) // oops
val v2 = new Base.ForH(): Base.Aux[HNil]
T3.summon(v2) // oops
}
it("Aux4") {
val v = new Base.ForH()
T4.summon(v) // oops
T4.summon(Base.ForH) // oops
val v2 = new Base.ForH(): Base.Aux[HNil]
T4.summon(v2) // oops
}
的所有实现都TypeClasses
包含其底层的隐式范围TypeClass
,其中,T1
是最简单和具体的定义ForH
,不幸的是它不起作用。@Dan Simon (in ) 提出了一项改进T2
,它使用类型参数来允许 spark 编译器发现ForH <:< Base.Aux[HNil]
现在想象一下,我想扩展@Dan Simon 的解决方案,以便类型类适用于所有类,例如ForH
不同种类的 HList(HNil 的超级特征)。2 个自然扩展分别在T3
&T4
中。
不幸的是,它们都不起作用。可以用无效的事实来解释,但不能T4
以此为借口。另外,没有办法改进它以编译成功。ForH <:< Aux[HList]
T3
为什么类型类召唤算法失败了?应该怎么做才能使类型类模式真正起作用?
解决方案
同样,T1.summon(v)
由于T1.t1
不是候选者,因此无法编译,手动解析T1.summon(v)(T1.t1)
无法编译。
对于T3
和T4
T3.t1[HNil, Base.ForH]
,T4.t1[HNil, Base.ForH]
将是一个候选者
T3.summon(v)(T3.t1[HNil, Base.ForH]) // compiles
T4.summon(v)(T4.t1[HNil, Base.ForH]) // compiles
但问题是H
首先推断它,它被推断为Nothing
但t1[Nothing, Base.ForH]
不满足类型界限。
所以问题不在于隐式解析算法,没关系,问题在于类型推断(我们都知道它在 Scala 中非常弱)。
你可以防止H
被推断过快,Nothing
如果你修改T3.t1
,T4.t1
object T3 extends TypeClasses {
implicit def t1[
H <: HList,
T <: Base/*.Lt[H]*/
](implicit ev: T <:< Base.Lt[H]): TypeClass[T] = new TypeClass[T] {
type HH = H
}
}
object T4 extends TypeClasses {
implicit def t1[
H <: HList,
T <: Base/*.Aux[H]*/
](implicit ev: T <:< Base.Aux[H]): TypeClass[T] = new TypeClass[T] {
type HH = H
}
}
T3.summon(v) // compiles
T4.summon(v) // compiles
推荐阅读
- python - 有没有办法在同一个环境中安装 CUDA 9 和 10 以同时运行 Tensorflow 2.0 和早期版本?
- google-sheets - 当中间有某个字符时,分别制作图表计数字符串
- macos - Docker:无法启动服务应用程序:OCI 运行时创建失败:
- c# - 我应该使用哪个工具以 Microsoft Speaker Recognition API 支持的格式录制音频(或转换录制的音频)?
- android - 膨胀类 com.google.android.material.navigation.NavigationView 的二进制 XML 错误
- json - 如何在postgresql中查询嵌套json数组
- python - 在 python 中建立和重新连接到 websocket 的更好方法是什么?
- android - 下载文件时,URLConnection 两次命中服务器?
- python - 匹配 EmbedDocuments 中的两个字段
- machine-learning - 如何同时使用交叉验证和提前停止?