ฉันคิดว่าประเภท disjoint ชั้นหนึ่งเป็น supertype ที่ถูกปิดผนึกพร้อมด้วยชนิดย่อยอื่นและการแปลงโดยนัยถึง / จากประเภทที่ต้องการของความแตกต่างไปยังประเภทย่อยทางเลือกเหล่านี้
ฉันถือว่าที่อยู่นี้แสดงความคิดเห็นที่ 33 - 36 ของวิธีแก้ปัญหาของ Miles Sabin ดังนั้นประเภทชั้นหนึ่งที่สามารถใช้ในไซต์การใช้งานได้ แต่ฉันไม่ได้ทดสอบ
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
ปัญหาหนึ่งคือสกาล่าจะไม่จ้างในบริบทกรณีการจับคู่แปลงนัยจากIntOfIntOrString
ไปInt
(และStringOfIntOrString
จะString
) ดังนั้นต้องกำหนดสกัดและใช้แทนcase Int(i)
case i : Int
เพิ่ม: ฉันตอบสนองต่อ Miles Sabin ที่บล็อกของเขาดังนี้ บางทีอาจมีการปรับปรุงหลายอย่างเช่น:
- มันขยายไปมากกว่า 2 ประเภทโดยไม่มีเสียงรบกวนเพิ่มเติมใด ๆ ในการใช้งานหรือไซต์คำจำกัดความ
- อาร์กิวเมนต์จะถูกบรรจุไว้ในกล่องโดยปริยายเช่นไม่ต้องการ
size(Left(2))
หรือsize(Right("test"))
หรือ
- ไวยากรณ์ของการจับคู่รูปแบบถูกยกเลิกโดยปริยาย
- การชกมวยและการ unbox อาจถูกปรับให้เหมาะสมโดยฮอตสปอต JVM
- ไวยากรณ์อาจเป็นรูปแบบที่นำไปใช้โดยประเภทสหภาพชั้นหนึ่งในอนาคตดังนั้นการโยกย้ายอาจเป็นไปอย่างราบรื่นหรือไม่ บางทีสำหรับชื่อประเภทยูเนี่ยนมันจะดีกว่าที่จะใช้
V
แทนOr
เช่นIntVString
` Int |v| String
` ` Int or String
` `หรือ` Int|String
`ที่ฉันโปรดปราน'?
UPDATE: ปฏิเสธตรรกะของความร้าวฉานสำหรับรูปแบบดังกล่าวข้างต้นต่อไปนี้และผมเพิ่มทางเลือก (และอาจจะมีประโยชน์มากขึ้น) รูปแบบที่บล็อกไมล์ซาบินของ
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)
การอัปเดตอีกครั้ง: เกี่ยวกับความคิดเห็นที่ 23 และ 35 ของวิธีแก้ปัญหาของMile Sabin ต่อไปนี้เป็นวิธีการประกาศประเภทสหภาพที่ไซต์การใช้งาน โปรดทราบว่ามันไม่มีกล่องหลังจากระดับแรกคือมันมีข้อได้เปรียบที่สามารถขยายออกไปได้หลายประเภทในความแตกต่างในขณะที่Either
ความต้องการมวยซ้อนกันและกระบวนทัศน์ในความคิดเห็นก่อนหน้าของฉัน 41 ไม่ขยาย ในคำอื่น ๆD[Int ∨ String]
คือการมอบหมาย 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
อยู่ในตำแหน่ง covariant อาจมีการโต้แย้งว่าเป็นข้อผิดพลาดเพราะสิ่งที่เราต้องการคือหลักฐานเราไม่เคยเข้าถึงหลักฐานในฟังก์ชั่น และฉันได้เลือกที่จะไม่ทดสอบcase _
ในget
วิธีดังนั้นฉันจะได้ไม่ต้อง unbox Option
ในในmatch
size()
5 มีนาคม 2012: การอัปเดตก่อนหน้าต้องมีการปรับปรุง วิธีแก้ปัญหาของ 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
ใน(() => A) => A
ปรากฏขึ้นทั้งใน covariant (ชนิดกลับ) และ contravariant (input ฟังก์ชั่นหรือในกรณีนี้ค่าตอบแทนของฟังก์ชั่นซึ่งเป็นฟังก์ชั่นการป้อนข้อมูล) ตำแหน่งจึงสามารถแทนเพียง แต่จะคงที่
ทราบว่าA => Nothing
เป็นสิ่งจำเป็นเท่านั้นเพราะเราต้องการA
อยู่ในตำแหน่ง contravariant เพื่อให้ supertypes ของA
ไม่ได้ชนิดย่อยของD[¬[A]]
มิได้D[¬[A] with ¬[U]]
( ดูเพิ่มเติมที่ ) เนื่องจากเราจะต้อง contravariance คู่เราสามารถบรรลุเทียบเท่ากับการแก้ปัญหาไมล์แม้ว่าเราสามารถทิ้งและ¬
∨
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]
}
สังเกตข้อบกพร่อง 2 ตัวก่อนหน้าใน Scala ยังคงอยู่ แต่ตัวที่ 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
ฉันได้รับการคิดว่าชนิดแยกชั้นแรกมีความสำคัญมากทั้งสำหรับเหตุผลที่ประเทศศรีลังกามีพวกเขาและเพราะแทนที่จะsubsumingไปAny
ซึ่งหมายความว่า unboxing กับmatch
ประเภทคาดว่าสามารถสร้างข้อผิดพลาด runtime ที่แกะกล่องของ ( คอลเลกชันที่แตกต่างกันมี ก) สามารถแยกประเภทการตรวจสอบได้ (Scala ต้องแก้ไขข้อบกพร่องที่ฉันบันทึกไว้) สหภาพแรงงานนั้นตรงไปตรงมามากกว่าความซับซ้อนของการใช้HList แบบทดลองของMetascalaสำหรับคอลเลกชันที่ต่างกัน
class StringOrInt[T]
ทำsealed
"รั่วไหล" ที่คุณอ้างถึง ("แน่นอนว่านี่อาจเป็นไปได้ที่โค้ดไคลเอ็นต์โดยการสร้างStringOrInt[Boolean]
") ถูกเสียบอย่างน้อยถ้าStringOrInt
อยู่ในไฟล์ของตัวเอง จากนั้นวัตถุพยานที่จะต้องกำหนดไว้ใน souceStringOrInt
เดียวกับ