如何使用 shapeless 创建具有依赖类型的类型类实例
How to create an instances for typeclass with dependent type using shapeless
我正在尝试为具有依赖类型的类型 class 派生元组实例。我正在使用 shapeless 为元组元素创建 class 类型。我在匹配元组实例类型时遇到问题:
import shapeless.the
import simulacrum.typeclass
@typeclass trait Identifiable[M] {
type K
def identify(id: M): K
}
object Identifiable{
implicit def identifiableTuple[K1: Identifiable, K2: Identifiable]: Identifiable[(K1,K2)] = new Identifiable[(K1,K2)]{
val b = the[Identifiable[K2]]
val a = the[Identifiable[K1]]
type K = (a.K, b.K)
override def identify(id: (K1, K2)): K = {
val k1 = the[Identifiable[K1]].identify(id._1)
val k2 = the[Identifiable[K2]].identify(id._2)
(k1,k2)
}
}
我收到这个错误:
type mismatch;
found : k1.type (with underlying type ai.fugo.cms.service.common.domain.Identifiable[K2]#K)
required: this.a.K
type mismatch;
found : k2.type (with underlying type ai.fugo.cms.service.common.domain.Identifiable[K1]#K)
required: this.b.K
尝试Aux
模式
trait Identifiable[M] {
type K
def identify(id: M): K
}
object Identifiable {
type Aux[M, K0] = Identifiable[M] { type K = K0 }
implicit def identifiableTuple[M1, K1, M2, K2](
implicit
identifiable1: Identifiable.Aux[M1, K1],
identifiable2: Identifiable.Aux[M2, K2]
): Identifiable.Aux[(M1, M2), (K1, K1)] = new Identifiable[(M1, M2)] {
type K = (K1, K2)
def identify(id: (M1, M2)): (K1, K2) =
identifiable1.identify(id._1) -> identifiable2.identify(id._2)
}
}
发明辅助模式是因为
- 人类更容易阅读
- 我认为(?)编译器过去在派生类型 类 路径相关类型时遇到问题......但不是他们的别名
所以只需使用 Aux
推导即可。
您的代码中有几处错误。
首先,如果你return(k1, k2)
那么k1
,k2
应该是the[Identifiable[K1]].identify(id._1)
, the[Identifiable[K2]].identify(id._2)
相应地而不是你定义的相反。(错别字已修复。)
其次,您忘记了类型优化。您将 identifiableTuple
的 return 类型声明为 Identifiable[(K1,K2)]
而不是正确的 Identifiable[(K1,K2)] { type K = (a.K, b.K)}
(又名 Identifiable.Aux[(K1,K2), (a.K, b.K)]
)。如果你保持 Identifiable[(K1,K2)]
你实际上是右手边
new Identifiable[(K1,K2)]{
...
type K = (a.K, b.K)
...
}
以及此隐式实例的信息 type K = (a.K, b.K)
将丢失。
因为你必须恢复类型细化你不能用上下文边界写identifiableTuple
,你必须用隐式块写它
implicit def identifiableTuple[K1, K2](implicit
a: Identifiable[K1],
b: Identifiable[K2]
): Identifiable[(K1, K2)] {type K = (a.K, b.K)} = new Identifiable[(K1, K2)] {
type K = (a.K, b.K)
override def identify(id: (K1, K2)): K = {
val k1 = a.identify(id._1)
val k2 = b.identify(id._2)
(k1, k2)
}
}
您可以在编译时测试您的代码
implicit val int: Identifiable[Int] { type K = Double } = null
implicit val str: Identifiable[String] { type K = Char } = null
implicitly[Identifiable[(Int, String)] { type K = (Double, Char)}]
你可以用 Aux
模式重写这个 type Aux[M, K0] = Identifiable[M] { type K = K0 }
implicit def identifiableTuple[K1, K2](implicit
a: Identifiable[K1],
b: Identifiable[K2]
): Identifiable.Aux[(K1, K2), (a.K, b.K)] = new Identifiable[(K1, K2)] {
type K = (a.K, b.K)
override def identify(id: (K1, K2)): K = {
val k1 = a.identify(id._1)
val k2 = b.identify(id._2)
(k1, k2)
}
} // (*)
和
implicit val int: Identifiable.Aux[Int, Double] = null
implicit val str: Identifiable.Aux[String, Char] = null
implicitly[Identifiable.Aux[(Int, String), (Double, Char)]]
这类似于@MateuszKubuszok的回答
implicit def identifiableTuple[M1, M2, K1, K2](implicit
a: Identifiable.Aux[M1, K1],
b: Identifiable.Aux[M2, K2]
): Identifiable.Aux[(M1, M2), (K1, K2)] = new Identifiable[(M1, M2)] {
type K = (K1, K2)
override def identify(id: (M1, M2)): K = {
val k1 = a.identify(id._1)
val k2 = b.identify(id._2)
(k1, k2)
}
} // (**)
虽然后者需要额外推断两种类型参数
第三, 你不能用 implicitly
甚至 the
来写 (*),比如
implicit def identifiableTuple[K1, K2](implicit
a: Identifiable[K1],
b: Identifiable[K2]
): Identifiable.Aux[(K1, K2), (a.K, b.K)] = new Identifiable[(K1, K2)] {
type K = (a.K, b.K)
override def identify(id: (K1, K2)): K = {
val k1 = the[Identifiable[K1]].identify(id._1)
val k2 = the[Identifiable[K2]].identify(id._2)
(k1, k2)
}
}
问题是在 Scala 中定义了依赖于路径的类型,因此即使 a == a1
、b == b1
类型为 a.K
和 a1.K
、b.K
和 b1.K
不同(a1
、b1
是 the[Identifiable[K1]]
、the[Identifiable[K2]]
)。所以你 return (k1, k2)
类型错误 (a1.K,b1.K)
.
但是如果你写成 (**) 风格
implicit def identifiableTuple[M1, M2, K1, K2](implicit
a: Identifiable.Aux[M1, K1],
b: Identifiable.Aux[M2, K2]
): Identifiable.Aux[(M1, M2), (K1, K2)] = new Identifiable[(M1, M2)] {
type K = (K1, K2)
override def identify(id: (M1, M2)): K = {
val k1 = the[Identifiable[M1]].identify(id._1)
val k2 = the[Identifiable[M2]].identify(id._2)
(k1, k2)
}
}
那么它就可以了(使用 the
但不能使用 implicitly
)因为编译器推断 (k1,k2)
的类型为 (K1,K2)
.
我正在尝试为具有依赖类型的类型 class 派生元组实例。我正在使用 shapeless 为元组元素创建 class 类型。我在匹配元组实例类型时遇到问题:
import shapeless.the
import simulacrum.typeclass
@typeclass trait Identifiable[M] {
type K
def identify(id: M): K
}
object Identifiable{
implicit def identifiableTuple[K1: Identifiable, K2: Identifiable]: Identifiable[(K1,K2)] = new Identifiable[(K1,K2)]{
val b = the[Identifiable[K2]]
val a = the[Identifiable[K1]]
type K = (a.K, b.K)
override def identify(id: (K1, K2)): K = {
val k1 = the[Identifiable[K1]].identify(id._1)
val k2 = the[Identifiable[K2]].identify(id._2)
(k1,k2)
}
}
我收到这个错误:
type mismatch;
found : k1.type (with underlying type ai.fugo.cms.service.common.domain.Identifiable[K2]#K)
required: this.a.K
type mismatch;
found : k2.type (with underlying type ai.fugo.cms.service.common.domain.Identifiable[K1]#K)
required: this.b.K
尝试Aux
模式
trait Identifiable[M] {
type K
def identify(id: M): K
}
object Identifiable {
type Aux[M, K0] = Identifiable[M] { type K = K0 }
implicit def identifiableTuple[M1, K1, M2, K2](
implicit
identifiable1: Identifiable.Aux[M1, K1],
identifiable2: Identifiable.Aux[M2, K2]
): Identifiable.Aux[(M1, M2), (K1, K1)] = new Identifiable[(M1, M2)] {
type K = (K1, K2)
def identify(id: (M1, M2)): (K1, K2) =
identifiable1.identify(id._1) -> identifiable2.identify(id._2)
}
}
发明辅助模式是因为
- 人类更容易阅读
- 我认为(?)编译器过去在派生类型 类 路径相关类型时遇到问题......但不是他们的别名
所以只需使用 Aux
推导即可。
您的代码中有几处错误。
首先,如果你return(错别字已修复。)(k1, k2)
那么k1
,k2
应该是the[Identifiable[K1]].identify(id._1)
, the[Identifiable[K2]].identify(id._2)
相应地而不是你定义的相反。
其次,您忘记了类型优化。您将 identifiableTuple
的 return 类型声明为 Identifiable[(K1,K2)]
而不是正确的 Identifiable[(K1,K2)] { type K = (a.K, b.K)}
(又名 Identifiable.Aux[(K1,K2), (a.K, b.K)]
)。如果你保持 Identifiable[(K1,K2)]
你实际上是右手边
new Identifiable[(K1,K2)]{
...
type K = (a.K, b.K)
...
}
以及此隐式实例的信息 type K = (a.K, b.K)
将丢失。
因为你必须恢复类型细化你不能用上下文边界写identifiableTuple
,你必须用隐式块写它
implicit def identifiableTuple[K1, K2](implicit
a: Identifiable[K1],
b: Identifiable[K2]
): Identifiable[(K1, K2)] {type K = (a.K, b.K)} = new Identifiable[(K1, K2)] {
type K = (a.K, b.K)
override def identify(id: (K1, K2)): K = {
val k1 = a.identify(id._1)
val k2 = b.identify(id._2)
(k1, k2)
}
}
您可以在编译时测试您的代码
implicit val int: Identifiable[Int] { type K = Double } = null
implicit val str: Identifiable[String] { type K = Char } = null
implicitly[Identifiable[(Int, String)] { type K = (Double, Char)}]
你可以用 Aux
模式重写这个 type Aux[M, K0] = Identifiable[M] { type K = K0 }
implicit def identifiableTuple[K1, K2](implicit
a: Identifiable[K1],
b: Identifiable[K2]
): Identifiable.Aux[(K1, K2), (a.K, b.K)] = new Identifiable[(K1, K2)] {
type K = (a.K, b.K)
override def identify(id: (K1, K2)): K = {
val k1 = a.identify(id._1)
val k2 = b.identify(id._2)
(k1, k2)
}
} // (*)
和
implicit val int: Identifiable.Aux[Int, Double] = null
implicit val str: Identifiable.Aux[String, Char] = null
implicitly[Identifiable.Aux[(Int, String), (Double, Char)]]
这类似于@MateuszKubuszok的回答
implicit def identifiableTuple[M1, M2, K1, K2](implicit
a: Identifiable.Aux[M1, K1],
b: Identifiable.Aux[M2, K2]
): Identifiable.Aux[(M1, M2), (K1, K2)] = new Identifiable[(M1, M2)] {
type K = (K1, K2)
override def identify(id: (M1, M2)): K = {
val k1 = a.identify(id._1)
val k2 = b.identify(id._2)
(k1, k2)
}
} // (**)
虽然后者需要额外推断两种类型参数
第三, 你不能用 implicitly
甚至 the
来写 (*),比如
implicit def identifiableTuple[K1, K2](implicit
a: Identifiable[K1],
b: Identifiable[K2]
): Identifiable.Aux[(K1, K2), (a.K, b.K)] = new Identifiable[(K1, K2)] {
type K = (a.K, b.K)
override def identify(id: (K1, K2)): K = {
val k1 = the[Identifiable[K1]].identify(id._1)
val k2 = the[Identifiable[K2]].identify(id._2)
(k1, k2)
}
}
问题是在 Scala 中定义了依赖于路径的类型,因此即使 a == a1
、b == b1
类型为 a.K
和 a1.K
、b.K
和 b1.K
不同(a1
、b1
是 the[Identifiable[K1]]
、the[Identifiable[K2]]
)。所以你 return (k1, k2)
类型错误 (a1.K,b1.K)
.
但是如果你写成 (**) 风格
implicit def identifiableTuple[M1, M2, K1, K2](implicit
a: Identifiable.Aux[M1, K1],
b: Identifiable.Aux[M2, K2]
): Identifiable.Aux[(M1, M2), (K1, K2)] = new Identifiable[(M1, M2)] {
type K = (K1, K2)
override def identify(id: (M1, M2)): K = {
val k1 = the[Identifiable[M1]].identify(id._1)
val k2 = the[Identifiable[M2]].identify(id._2)
(k1, k2)
}
}
那么它就可以了(使用 the
但不能使用 implicitly
)因为编译器推断 (k1,k2)
的类型为 (K1,K2)
.