我认为第一类不相交类型是密封的超类型,具有备用子类型,并且隐含转换到或从所需的分离形式转换为这些备用子类型。
我认为这解决了Miles Sabin解决方案的第33-36条评论,因此可以在使用站点使用一流的类型,但是我没有对其进行测试。
sealed trait IntOrString
case class IntOfIntOrString( v:Int ) extends IntOrString
case class StringOfIntOrString( v:String ) extends IntOrString
implicit def IntToIntOfIntOrString( v:Int ) = new IntOfIntOrString(v)
implicit def StringToStringOfIntOrString( v:String ) = new StringOfIntOrString(v)
object Int {
def unapply( t : IntOrString ) : Option[Int] = t match {
case v : IntOfIntOrString => Some( v.v )
case _ => None
}
}
object String {
def unapply( t : IntOrString ) : Option[String] = t match {
case v : StringOfIntOrString => Some( v.v )
case _ => None
}
}
def size( t : IntOrString ) = t match {
case Int(i) => i
case String(s) => s.length
}
scala> size("test")
res0: Int = 4
scala> size(2)
res1: Int = 2
一个问题是,Scala不会在大小写匹配的上下文中使用,即从IntOfIntOrString
to Int
(和StringOfIntOrString
to String
)的隐式转换,因此必须定义提取器并使用case Int(i)
代替case i : Int
。
添加:我在他的博客中回复了Miles Sabin,如下所示。也许对Either有一些改进:
- 它扩展到两种以上的类型,在使用或定义站点上没有任何其他噪音。
- 参数被隐式装箱,例如,不需要
size(Left(2))
或size(Right("test"))
。
- 模式匹配的语法隐式取消装箱。
- 装箱和拆箱可以通过JVM热点进行优化。
- 该语法可能是将来的一流联合类型采用的语法,因此迁移可能是无缝的?也许对于联合体类型名称,最好使用
V
代替Or
,例如IntVString
` Int |v| String
`,` Int or String
`或我最喜欢的` Int|String
`?
更新:上面的模式的逻辑取反如下所示,我在Miles Sabin的博客中添加了另一种(可能更有用的)模式。
sealed trait `Int or String`
sealed trait `not an Int or String`
sealed trait `Int|String`[T,E]
case class `IntOf(Int|String)`( v:Int ) extends `Int|String`[Int,`Int or String`]
case class `StringOf(Int|String)`( v:String ) extends `Int|String`[String,`Int or String`]
case class `NotAn(Int|String)`[T]( v:T ) extends `Int|String`[T,`not an Int or String`]
implicit def `IntTo(IntOf(Int|String))`( v:Int ) = new `IntOf(Int|String)`(v)
implicit def `StringTo(StringOf(Int|String))`( v:String ) = new `StringOf(Int|String)`(v)
implicit def `AnyTo(NotAn(Int|String))`[T]( v:T ) = new `NotAn(Int|String)`[T](v)
def disjunction[T,E](x: `Int|String`[T,E])(implicit ev: E =:= `Int or String`) = x
def negationOfDisjunction[T,E](x: `Int|String`[T,E])(implicit ev: E =:= `not an Int or String`) = x
scala> disjunction(5)
res0: Int|String[Int,Int or String] = IntOf(Int|String)(5)
scala> disjunction("")
res1: Int|String[String,Int or String] = StringOf(Int|String)()
scala> disjunction(5.0)
error: could not find implicit value for parameter ev: =:=[not an Int or String,Int or String]
disjunction(5.0)
^
scala> negationOfDisjunction(5)
error: could not find implicit value for parameter ev: =:=[Int or String,not an Int or String]
negationOfDisjunction(5)
^
scala> negationOfDisjunction("")
error: could not find implicit value for parameter ev: =:=[Int or String,not an Int or String]
negationOfDisjunction("")
^
scala> negationOfDisjunction(5.0)
res5: Int|String[Double,not an Int or String] = NotAn(Int|String)(5.0)
另一个更新:关于Mile Sabin解决方案的注释23和35 ,这是一种在使用站点声明联合类型的方法。请注意,在第一级之后将其取消装箱,即,它的优点是可扩展为析取运算符中的任何类型,而Either
需要嵌套装箱,而我先前的评论41中的范式则不可扩展。换句话说,a D[Int ∨ String]
可分配给a(即a的子类型)D[Int ∨ String ∨ Double]
。
type ¬[A] = (() => A) => A
type ∨[T, U] = ¬[T] with ¬[U]
class D[-A](v: A) {
def get[T](f: (() => T)) = v match {
case x : ¬[T] => x(f)
}
}
def size(t: D[Int ∨ String]) = t match {
case x: D[¬[Int]] => x.get( () => 0 )
case x: D[¬[String]] => x.get( () => "" )
case x: D[¬[Double]] => x.get( () => 0.0 )
}
implicit def neg[A](x: A) = new D[¬[A]]( (f: (() => A)) => x )
scala> size(5)
res0: Any = 5
scala> size("")
error: type mismatch;
found : java.lang.String("")
required: D[?[Int,String]]
size("")
^
scala> size("hi" : D[¬[String]])
res2: Any = hi
scala> size(5.0 : D[¬[Double]])
error: type mismatch;
found : D[(() => Double) => Double]
required: D[?[Int,String]]
size(5.0 : D[?[Double]])
^
显然,Scala编译器存在三个错误。
- 在目标析取中的第一个类型之后,它将不会为任何类型选择正确的隐式函数。
- 它不会
D[¬[Double]]
从比赛中排除这种情况。
3。
scala> class D[-A](v: A) {
def get[T](f: (() => T))(implicit e: A <:< ¬[T]) = v match {
case x : ¬[T] => x(f)
}
}
error: contravariant type A occurs in covariant position in
type <:<[A,(() => T) => T] of value e
def get[T](f: (() => T))(implicit e: A <:< ?[T]) = v match {
^
get方法未正确限制在输入类型上,因为编译器不允许A
在协变位置上使用。有人可能会认为这是一个错误,因为我们想要的只是证据,而我们从不访问函数中的证据。我做出的选择不测试case _
的get
方法,所以我不会有拆箱一Option
中match
的size()
。
2012年3月5日:之前的更新需要改进。Miles Sabin的解决方案可以正确地进行子类型化。
type ¬[A] = A => Nothing
type ∨[T, U] = ¬[T] with ¬[U]
class Super
class Sub extends Super
scala> implicitly[(Super ∨ String) <:< ¬[Super]]
res0: <:<[?[Super,String],(Super) => Nothing] =
scala> implicitly[(Super ∨ String) <:< ¬[Sub]]
res2: <:<[?[Super,String],(Sub) => Nothing] =
scala> implicitly[(Super ∨ String) <:< ¬[Any]]
error: could not find implicit value for parameter
e: <:<[?[Super,String],(Any) => Nothing]
implicitly[(Super ? String) <:< ?[Any]]
^
我先前的更新建议(针对近乎一流的联合体类型)打破了子类型化。
scala> implicitly[D[¬[Sub]] <:< D[(Super ∨ String)]]
error: could not find implicit value for parameter
e: <:<[D[(() => Sub) => Sub],D[?[Super,String]]]
implicitly[D[?[Sub]] <:< D[(Super ? String)]]
^
问题在于A
in (() => A) => A
出现在协变(返回类型)和逆变(函数输入,或者在这种情况下是函数输入的函数的返回值)位置中,因此替换只能是不变的。
请注意,A => Nothing
仅仅是因为我们要的是需要A
在逆变位置,这样的超类型A
不亚型的D[¬[A]]
也不是D[¬[A] with ¬[U]]
(另见)。由于我们只需要双重逆方差,即使我们可以丢弃¬
和,我们也可以实现与Miles解决方案相同的解决方案∨
。
trait D[-A]
scala> implicitly[D[D[Super]] <:< D[D[Super] with D[String]]]
res0: <:<[D[D[Super]],D[D[Super] with D[String]]] =
scala> implicitly[D[D[Sub]] <:< D[D[Super] with D[String]]]
res1: <:<[D[D[Sub]],D[D[Super] with D[String]]] =
scala> implicitly[D[D[Any]] <:< D[D[Super] with D[String]]]
error: could not find implicit value for parameter
e: <:<[D[D[Any]],D[D[Super] with D[String]]]
implicitly[D[D[Any]] <:< D[D[Super] with D[String]]]
^
因此,完整的修复方法是。
class D[-A] (v: A) {
def get[T <: A] = v match {
case x: T => x
}
}
implicit def neg[A](x: A) = new D[D[A]]( new D[A](x) )
def size(t: D[D[Int] with D[String]]) = t match {
case x: D[D[Int]] => x.get[D[Int]].get[Int]
case x: D[D[String]] => x.get[D[String]].get[String]
case x: D[D[Double]] => x.get[D[Double]].get[Double]
}
请注意,Scala中的前2个bug仍然存在,但避免了第3个bug,因为T
现在将其限制为的子类型A
。
我们可以确认子类型化工作。
def size(t: D[D[Super] with D[String]]) = t match {
case x: D[D[Super]] => x.get[D[Super]].get[Super]
case x: D[D[String]] => x.get[D[String]].get[String]
}
scala> size( new Super )
res7: Any = Super@1272e52
scala> size( new Sub )
res8: Any = Sub@1d941d7
我一直在想的是一流的路口类型是非常重要的,无论对于原因锡兰有他们,因为不是归并到Any
该装置与拆箱match
预期的类型可以产生一个运行时错误,拆箱一(的均质集合体含a)分离可以进行类型检查(Scala必须修复我指出的错误)。工会比更直接使用的复杂实验HList的metascala异构集合。
class StringOrInt[T]
进行了sealed
,则插入了您所引用的“泄漏”(“当然,可以通过创建StringOrInt[Boolean]
“ 来使客户端代码避开此泄漏),至少如果StringOrInt
驻留在其自己的文件中。然后,必须在相同的来源中定义见证对象StringOrInt
。