ファーストクラスのディスジョイントタイプは、別のサブタイプを持つ封印されたスーパータイプであり、これらの代替サブタイプへの分離型の望ましいタイプとの間の暗黙の変換であると考えています。
これは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
1つの問題は、Scalaが大文字と小文字を区別するコンテキスト、つまりIntOfIntOrString
toからInt
(およびStringOfIntOrString
toへString
)の暗黙的な変換を採用しないため、エクストラクターを定義してのcase Int(i)
代わりに使用する必要があることcase i : Int
です。
追加:私は彼のブログでMiles Sabinに次のように返信しました。おそらく、次のいずれかに対していくつかの改善点があります。
- それは、使用または定義サイトで追加のノイズなしで、2種類以上に拡張されます。
- 引数は暗黙的にボックス化されます。たとえば、
size(Left(2))
or は必要ありません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コンパイラーには3つのバグがある。
- 宛先の選言で最初の型の後の型に対して正しい暗黙の関数を選択しません。
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 {
^
コンパイラーがA
共変位置で許可しないため、getメソッドは入力タイプで適切に制約されていません。必要なのは証拠だけなので、それはバグであると主張する人もいるかもしれません。関数の証拠にアクセスすることはありません。そして、私case _
はget
メソッドでテストしないことを選択したので、Option
inのボックスを解除する必要はありませんでし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]]
^
私の以前の更新の提案(ほぼ一流のunionタイプについて)はサブタイプを壊しました。
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
で(() => A) => A
が共変(戻りの型)と反変(関数の入力、またはこの場合は関数の入力である関数の戻り値)の両方の位置に表示されるため、置換は不変にしかできないことです。
A => Nothing
が必要なのA
は、反変の位置にしたいためだけであることに注意してください。これにより、のスーパーA
タイプは、D[¬[A]]
norのサブタイプではなくなりますD[¬[A] with ¬[U]]
(も参照)。必要なのは二重の逆分散だけなので、¬
and を破棄しても、マイルの解と同等の結果を得ることができ∨
ます。
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つのバグは残っていますが、3番目のバグは回避され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は私が指摘したバグを修正する必要があります)。ユニオンは、異種のコレクションにmetascalaの実験的なHListを使用する複雑さよりも簡単です。
class StringOrInt[T]
行われsealed
、「漏れ」は、あなたが(「もちろん、これはサイドステップ作成することにより、クライアントコードで可能性に言及StringOrInt[Boolean]
、接続されている」)少なくともあればStringOrInt
、独自のファイルに存在します。次に、ウィットネスオブジェクトをと同じソースで定義する必要がありますStringOrInt
。