如何在Scala中获取结构联合类型?
我不确定“结构联合类型”是否是正确的术语,但我试图在Scala中获得以下duck类型行为 假设我有一些容器类如何在Scala中获取结构联合类型?,scala,types,polymorphism,duck-typing,Scala,Types,Polymorphism,Duck Typing,我不确定“结构联合类型”是否是正确的术语,但我试图在Scala中获得以下duck类型行为 假设我有一些容器类container,其中包含获取A和B类型对象的方法: 类容器{ def getA:A def getB:B } A和B都有一个commontinfield属性,但它们也有一些非共享方法: A类{ val commonIntField:Int def getAStringField1:字符串 def getAStringField2:字符串 } B类{ val commonIntFiel
container
,其中包含获取A
和B
类型对象的方法:
类容器{
def getA:A
def getB:B
}
A
和B
都有一个commontinfield
属性,但它们也有一些非共享方法:
A类{
val commonIntField:Int
def getAStringField1:字符串
def getAStringField2:字符串
}
B类{
val commonIntField:Int
def getBStringField:字符串
}
现在假设我想定义以下三个函数,它们看起来非常相似:
def f1(c:容器)={
val a=c.getA
(0到a.commonIntField)foreach{println(a.getAStringField1)}
}
def f2(c:容器)={
val a=c.getA
(0到a.commonIntField)foreach{println(a.getAStringField2)}
}
def f2(c:容器)={
valb=c.getB
(0到b.commonIntField)foreach{println(a.getBStringField)}
}
我要找的是某种干燥方法,例如:
def(getAOrB,getStringField)(c:容器)={
val aOrB=c.getAOrB
(0到aOrB.commonIntField)foreach{println(aOrB.getStringField)}
}
val f1=f(u.getA,u.getAStringField1)
val f2=f(u.getA,u.getAStringField2)
val f3=f(u.getB,u.getBStringField)
我的问题:我可以使用什么类型参数来编译
f
?(这里假设我不能更改容器
、A
或B
的定义,尽管如果需要,我当然可以定义辅助类型。)您应该向f
添加一个类型参数并对其进行约束
因此,我们首先添加参数并对其进行相应的重构:
def f[T](tGetter: Container => T, getStringField: T => String)(c: Container) = {
val t: T = tGetter(c)
val intField = getIntField(t)
(0 to intField) foreach { println(stringGetter(t)) }
}
还有一个问题——我们如何定义从容器中获取T的方式?
更简单的方法-只需添加tGetter:Container=>T
作为第一个参数。这是一个附加参数,但很简单,它将帮助您自动派生类型参数的类型
def f[T](tGetter: Container => T, getStringField: T => String)(c: Container) = {
val t: T = tGetter(c)
val intField: Int = ??? // getIntField(t)
(0 to intField).foreach { _ => println(getStringField(t)) }
}
// val f1 = f(_.getA, _.getAStringField1) // the old one
val f1 = f[A](_.getA, _.getAStringField1)(_)
// val f2 = f(_.getA, _.getAStringField2)
val f2 = f[A](_.getA, _.getAStringField2)(_)
// val f3 = f(_.getB, _.getBStringField)
val f3 = f[B](_.getB, _.getBStringField)(_)
但是有一个问题-我们如何得到int字段?
我们有几种方法
1.结构分型
您只需像这样表达所需的结构类型{def commonIntField:Int}
,并为T编写类型上限:
def f[T <: { def commonIntField: Int }](tGetter: Container => T, getStringField: T => String)(c: Container) = {
然后,为希望在此类功能中支持的每个类创建该类型类的实例
object HaveIntField {
implicit val haveIntField_A_Instance: HaveIntField[A] = new HaveIntField[A] {
def getIntField(a: A): Int = a.commonIntField
}
implicit val haveIntField_A_Instance: HaveIntField[A] = new HaveIntField[A] {
def getIntField(a: A): Int = a.commonIntField
}
}
最后,在T上添加上下文绑定
def f[T: HaveIntField](tGetter: Container => T, getStringField: T => String)(c: Container) = {
val t: T = tGetter(c)
val intField = implicitly[HaveIntField[T]].getIntField(t)
0 to intField) foreach { println(stringGetter(t)) }
}
在Scala 2中,这看起来有点令人讨厌和难以忍受,但它是FP/Haskell提出的一个很酷的概念。在即将到来的Scala 3中,它将非常平滑
object HaveIntField {
implicit val haveIntField_A_Instance: HaveIntField[A] = new HaveIntField[A] {
def getIntField(a: A): Int = a.commonIntField
}
implicit val haveIntField_A_Instance: HaveIntField[A] = new HaveIntField[A] {
def getIntField(a: A): Int = a.commonIntField
}
}
def f[T: HaveIntField](tGetter: Container => T, getStringField: T => String)(c: Container) = {
val t: T = tGetter(c)
val intField = implicitly[HaveIntField[T]].getIntField(t)
0 to intField) foreach { println(stringGetter(t)) }
}