첫 번째 클래스 분리 유형은 대체 하위 유형이있는 봉인 된 슈퍼 유형이며 원하는 유형의 분리 유형에서 이러한 대체 하위 유형으로의 암시 적 변환이라고 생각합니다.
필자는 이것이 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
한 가지 문제는 스칼라의 경우 일치하는 맥락에서하지 고용에서 암시 적 변환 것이다 IntOfIntOrString
에 Int
(그리고 StringOfIntOrString
로는 String
), 그래서 추출기를 정의하고 사용해야합니다 case Int(i)
대신 case i : Int
.
ADD : 다음과 같이 블로그에서 Miles Sabin에 응답했습니다. 아마도 몇 가지 개선 사항이 있습니다.
- 사용 또는 정의 사이트에서 추가 노이즈없이 2 가지 이상의 유형으로 확장됩니다.
- 인수는 암시 적으로 상자에 표시됩니다 (예 :
size(Left(2))
또는 필요 없음) size(Right("test"))
.
- 패턴 일치 구문은 암시 적으로 언 박싱됩니다.
- JVM 핫스팟에 의해 boxing 및 unboxing이 최적화 될 수 있습니다.
- 구문은 미래의 일류 공용체 유형에서 채택 된 것일 수 있으므로 마이그레이션이 원활 할 수 있습니까? 아마도 공용체 타입 이름
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 에 할당 가능합니다 (즉, 하위 유형입니다) 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]])
^
분명히 스칼라 컴파일러에는 세 가지 버그가 있습니다.
- 대상 분리에서 첫 번째 유형 이후의 모든 유형에 대해 올바른 암시 적 기능을 선택하지 않습니다.
D[¬[Double]]
경기 에서 사건을 배제하지는 않습니다 .
삼.
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
에서을 match
에 size()
.
2012 년 3 월 5 일 : 이전 업데이트에는 개선이 필요합니다. 마일즈 사빈의 솔루션 은 서브 타이핑에서 올바르게 작동했습니다.
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 (return type)와 contravariant (function input 또는이 경우 function input 인 function의 return value) 위치 나타나기 때문에 치환은 변하지 않을 수 있다는 것입니다.
주 A => Nothing
우리가 원하는 때문 필요 A
contravariant 위치에, 그래서의 슈퍼 타입이 있다고 A
하지 아류 의 D[¬[A]]
도 D[¬[A] with ¬[U]]
( 도 참조 ). 이중 반공 분산 만 필요하기 때문에 ¬
and를 폐기 할 수 있어도 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]
}
스칼라의 이전 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
나는 일류 교차 유형이 매우 중요하다고 생각하고 한 모두 실론를 가지고 이유를 대신하기 때문에, 그리고 subsuming 에 Any
로모그래퍼 개봉기하는 수단 match
런타임 오류를 생성 할 수 있습니다 예상 유형에, (의 개봉기 이기종 컬렉션에 포함 a) 분리는 유형을 검사 할 수 있습니다 (Scala는 내가 언급 한 버그를 수정해야합니다). 이종 수집 에 메타 스칼라 의 실험적 HList 를 사용 하는 것보다 복잡한 것이 더 간단합니다 .
class StringOrInt[T]
만든 경우 , 적어도 " 자신의 파일에 상주하는 경우sealed
" "누설"( "물론 클라이언트 코드에 의해StringOrInt[Boolean]
" 단계적으로 생성 될 수 있음 ")이 연결StringOrInt
되어 있습니다. 그런 다음 감시 객체를와 동일한 소스에 정의해야합니다StringOrInt
.