scala中的配对类型参数和参数子类型参数
Pairing type argument and argument sub-type argument in scala
我发现实现可以巧妙地解决我的问题时遇到了麻烦:我想对一些 "converter" 的不同输出类型进行多个实现,但所有实现都共享一些架构定义。
单靠这个词很难解释,所以这里有一个示例代码:
sealed trait Schema[+A]
object Schema{
// Schema definition
object IntSchema extends Schema[Int]
// ...
case class SeqSchema[B](bSchema: Schema[B]) extends Schema[Seq[B]]
// ...
case class MappedSchema[A,B](aToB: A => B, bSchema: Schema[B]) extends Schema[A]
// ...
// base implementations of conversion to "Dst"
trait Dst // some output type, used below
def convertIntToDst(i: Int): Dst = ???
def convertSeqToDst[A](s: Seq[A]): Dst = ???
// ...
// *** here is what I want to do: ***
// combine base conversion using schema
def convertToDst[A](a: A, schema: Schema[A]): Dst = schema match {
case IntSchema =>
convertIntToDst(a.asInstanceOf[Int]) // (1) asInstanceOf :(
case s: SeqSchema[_] =>
convertSeqToDst(a.asInstanceOf[Seq[_]].map{ ai => // (2)
convertToDst(ai, s.bSchema) // (3)
})
case s: MappedSchema[_,_] =>
convertToDst(s.aToB(a), s.bSchema) // (4) fails to compile!!
}
// Would want to implement some conversion to other types, still using schema
}
如您所见,我无法将 converterToDst
的第一个参数的 A
类型 "pairing" 转换为 schema
参数的类型参数。但是,我认为从方法定义来看,两者可以一起使用应该是安全的。
我得到的错误是
[...] type mismatch;
[error] found : a.type (with underlying type A)
[error] required: _
convertToDst(s.aToB(a), s.bSchema)
所以我有两个问题:
- 如何使案例 (4) 编译通过?
- 不是最重要的,但是有没有办法不在标记为 (1) 和 (2) 的行上投射
a
?
我认为对于第一个问题,但仍然使用 asInstanceOf,如果我可以在匹配案例中命名类型参数,例如 case s: MappedSchema[A,B] =>
,我可以解决一些问题。但这看起来不可能(由于类型擦除,这可能是个坏主意)
另外注意,发现这个issue后,发现case(3)编译的时候很奇怪:(来自IntelliJ)ai
is Any
and c.bSchema
is Schema[B]
.
我认为使用 typeclasses.
会更加类型安全和可扩展
这是使用这种方法重写的代码,如果您在将其适应实际用例时遇到任何问题,请告诉我。
trait Dst
sealed trait Schema[A] {
def convertToDst(input: A): Dst
final def contraMap[B](bToA: B => A): Schema[B] = new Schema[B] {
override final def convertToDst(input: B): Dst =
this.convertToDst(input = bToA(input))
}
}
object Schema {
final implicit val IntSchema: Schema[Int] =
new Schema[Int] {
override final def convertToDst(input: Int): Dst =
???
}
implicit def listSchema[A](implicit aSchema: Schema[A]): Schema[List[A]] =
new Schema[List[A]] {
override final def convertToDst(input: List[A]): Dst =
???
}
def convertToDst[A](a: A)(implicit schema: Schema[A]): Dst =
schema.convertToDst(input = a)
}
您可以在模式中命名类型参数:
case s: SeqSchema[a1] =>
convertSeqToDst(a.asInstanceOf[Seq[a1]].map{ ai => // (2)
convertToDst(ai, s.bSchema) // (3)
})
case s: MappedSchema[a1,b1] =>
convertToDst(s.aToB(a.asInstanceOf[a1]), s.bSchema) // (4)
I found that it is strange that the case (3) compiles: (from intellij) ai is Any and c.bSchema is Schema[B].
因为您将 Schema
声明为协变 ([+A]
),所以 Schema[B]
也是 Schema[Any]
,无论 B
是什么。
您可以通过将第一个类型参数固定为 A
:
来帮助编译器推断类型
case s: MappedSchema[A, _] =>
convertToDst(s.aToB(a), s.bSchema)
适用于 Scala 2.13 和 2.12。
我发现实现可以巧妙地解决我的问题时遇到了麻烦:我想对一些 "converter" 的不同输出类型进行多个实现,但所有实现都共享一些架构定义。
单靠这个词很难解释,所以这里有一个示例代码:
sealed trait Schema[+A]
object Schema{
// Schema definition
object IntSchema extends Schema[Int]
// ...
case class SeqSchema[B](bSchema: Schema[B]) extends Schema[Seq[B]]
// ...
case class MappedSchema[A,B](aToB: A => B, bSchema: Schema[B]) extends Schema[A]
// ...
// base implementations of conversion to "Dst"
trait Dst // some output type, used below
def convertIntToDst(i: Int): Dst = ???
def convertSeqToDst[A](s: Seq[A]): Dst = ???
// ...
// *** here is what I want to do: ***
// combine base conversion using schema
def convertToDst[A](a: A, schema: Schema[A]): Dst = schema match {
case IntSchema =>
convertIntToDst(a.asInstanceOf[Int]) // (1) asInstanceOf :(
case s: SeqSchema[_] =>
convertSeqToDst(a.asInstanceOf[Seq[_]].map{ ai => // (2)
convertToDst(ai, s.bSchema) // (3)
})
case s: MappedSchema[_,_] =>
convertToDst(s.aToB(a), s.bSchema) // (4) fails to compile!!
}
// Would want to implement some conversion to other types, still using schema
}
如您所见,我无法将 converterToDst
的第一个参数的 A
类型 "pairing" 转换为 schema
参数的类型参数。但是,我认为从方法定义来看,两者可以一起使用应该是安全的。
我得到的错误是
[...] type mismatch;
[error] found : a.type (with underlying type A)
[error] required: _
convertToDst(s.aToB(a), s.bSchema)
所以我有两个问题:
- 如何使案例 (4) 编译通过?
- 不是最重要的,但是有没有办法不在标记为 (1) 和 (2) 的行上投射
a
?
我认为对于第一个问题,但仍然使用 asInstanceOf,如果我可以在匹配案例中命名类型参数,例如 case s: MappedSchema[A,B] =>
,我可以解决一些问题。但这看起来不可能(由于类型擦除,这可能是个坏主意)
另外注意,发现这个issue后,发现case(3)编译的时候很奇怪:(来自IntelliJ)ai
is Any
and c.bSchema
is Schema[B]
.
我认为使用 typeclasses.
会更加类型安全和可扩展这是使用这种方法重写的代码,如果您在将其适应实际用例时遇到任何问题,请告诉我。
trait Dst
sealed trait Schema[A] {
def convertToDst(input: A): Dst
final def contraMap[B](bToA: B => A): Schema[B] = new Schema[B] {
override final def convertToDst(input: B): Dst =
this.convertToDst(input = bToA(input))
}
}
object Schema {
final implicit val IntSchema: Schema[Int] =
new Schema[Int] {
override final def convertToDst(input: Int): Dst =
???
}
implicit def listSchema[A](implicit aSchema: Schema[A]): Schema[List[A]] =
new Schema[List[A]] {
override final def convertToDst(input: List[A]): Dst =
???
}
def convertToDst[A](a: A)(implicit schema: Schema[A]): Dst =
schema.convertToDst(input = a)
}
您可以在模式中命名类型参数:
case s: SeqSchema[a1] =>
convertSeqToDst(a.asInstanceOf[Seq[a1]].map{ ai => // (2)
convertToDst(ai, s.bSchema) // (3)
})
case s: MappedSchema[a1,b1] =>
convertToDst(s.aToB(a.asInstanceOf[a1]), s.bSchema) // (4)
I found that it is strange that the case (3) compiles: (from intellij) ai is Any and c.bSchema is Schema[B].
因为您将 Schema
声明为协变 ([+A]
),所以 Schema[B]
也是 Schema[Any]
,无论 B
是什么。
您可以通过将第一个类型参数固定为 A
:
case s: MappedSchema[A, _] =>
convertToDst(s.aToB(a), s.bSchema)
适用于 Scala 2.13 和 2.12。