2011-08-12 5 views
4

에 대한 실존 적 유형 I은 알려지지에 다양한 유형의지도를 사용하려면 :스칼라 :지도

val map: Map[Foo[A], Bar[A]] = ... 
... 
val foo = new Foo[Qux] 
val bar: Bar[Qux] = map(foo) 

이 작동하지 않는, A는 알 수 있기 때문이다. 대신 다음과 같이 정의해야합니다.

val map: Map[Foo[_], Bar[_]] = ... 
... 
val foo = new Foo[Qux] 
val bar: Bar[Qux] = map(foo).asInstanceOf[Bar[Qux]] 

이 작동하지만 캐스트는 추악합니다. 차라리 더 나은 방법을 찾고 싶습니다. 대답은 forSome 키워드로 실존 유형을 사용하는 것이지만 어떻게 작동하는지 혼란 스럽습니다. 그것은해야합니다

Map[Foo[A], Bar[A]] forSome { type A } 

나 :

Map[Foo[A] forSome { type A }, Bar[A]] 

나 :

Map[Foo[A forSome { type A }], Bar[A]] 

답변

8

사실,이 작품의 없음.

Map[Foo[A], Bar[A]] forSome { type A } 

모든 키가 동일 유형 및 값 유형 Foo[A]Bar[A]의 (그러나 A이 다른 유형의 매핑에 대해 상이 할 수있는 형식)으로되어 Map이고; 제 2 및 제 3 실시 예에서는 ABar[A]이고, AforSome과 완전히 다르다.

이 추한 해결 방법은 작동합니다 :

// need type members, so can't use tuples 
case class Pair[A, B](a: A, b: B) { 
    type T1 = A 
    type T2 = B 
} 

type PairedMap[P <: Pair[_, _]] = Map[P#T1, P#T2] 

type FooBarPair[A] = Pair[Foo[A], Bar[A]] 

val map: PairedMap[FooBarPair[_]] = ... 
+0

감사합니다 ... 그 후에 내가 무엇을 달성 할 수있는 아이디어가 있습니까? –

+0

@Marcus Downing : 편집 –

+0

보기.보기 흉하게 들립니다. 실제로 FooBarPair를 만들지 않고 그냥 파생 된 유형을 사용합니까? –

0

무엇에 대해 뭔가

type MyMap[A] = Map[Foo[A],Bar[A]] 
val map:MyMap[Qux] = ... 
... 
val foo = new Foo[Qux] 
val bar: Bar[Qux] = map(foo) 
+0

유형 A는 전체 모음에만 적용해서는 안되며 한 쌍만 적용해야하므로 적합하지 않습니다. 'Foo [Qux]'는'Map [Foo [_], Bar [_]]'의'Foo [Qux]'가'Bar [ Qux]. –

+0

아, 나는 원래 질문을 읽을 때 분명히 그것을 놓쳤다. –

1

I want to use a map of varying types on an unknown A

그래서, 당신이 원하는 (알렉세이 로마노프의 대답에 의해 영감을)

def map[A]: Map[Foo[A], Bar[A]] = ... 
val myMap = map[Qux] 
... 
val foo = new Foo[Qux] 
val bar: Bar[Qux] = myMap(foo) 

또는

처럼의 변형다음 인터페이스가 맞습니까? 유형 검사는 우리가 원하는 것을 우리가 동의 할 것을 허용하고 거부 할 경우

trait DependentMap[K[_],V[_]] { 
    def add[A](key: K[A], value: V[A]): DependentMap[K,V] 
    def get[A](key: K[A]): Option[V[A]] 
} 
이 있는지 여부

여부는 유형 서명에서 얘기, 그래서 몇 가지 더미 값을 생성하고 볼 수 있도록하기 어려운 약간 수 있습니다 거부하기.

// dummy definitions just to check that the types are correct 
case class Foo[+A](a: A) 
case class Bar[+A](a: A) 
val myMap: DependentMap[Foo,Bar] = null 

myMap.add(Foo( 42), Bar( 43)) // typechecks 
myMap.add(Foo("foo"), Bar("bar")) // typechecks 
myMap.add(Foo( 42), Bar("bar")) // type mismatch 

val r1: Option[Bar[ Int]] = myMap.get(Foo( 42)) // typechecks 
val r2: Option[Bar[String]] = myMap.get(Foo("foo")) // typechecks 
val r3: Option[Bar[String]] = myMap.get(Foo( 42)) // type mismatch 

지금까지는 그렇게 좋았습니다. 그러나 우리가 상속으로 재생 시작하면 어떻게되는지 살펴 :

val fooInt: Foo[Int] = Foo(42) 
val fooAny: Foo[Any] = fooInt 
val barStr: Bar[String] = Bar("bar") 
val barAny: Bar[Any] = barStr 
println(fooInt == fooAny) // true 
myMap.add(fooAny, barAny).get(fooInt) // Bar("bar")? 

fooInt 이후와 fooAny가 동일한 값, 우리는 순진하게도이 get 성공에 기대. get의 형식 서명에 따르면 Bar[Int] 유형의 값으로 성공해야하지만이 값의 출처는 어디입니까? 우리가 입력 한 값은 타입이 Bar[Any]이고 유형이 Bar[String]이지만 확실히 Bar[Int]이 아닙니다!

또 다른 놀라운 사례가 있습니다.

val fooListInt: Foo[List[Int]] = Foo(List[Int]()) 
val fooListStr: Foo[List[String]] = Foo(List[String]()) 
println(fooListInt == fooListStr) // true! 
myMap.add(fooListInt, Bar(List(42))).get(fooListStr) // Bar(List(42))? 

이 시간 fooListIntfooListStr보기가 별개해야하지만, 사실 둘 다 List[Int]List[String] 모두의 하위 유형 인 유형 List[Nothing],의 값 Nil을 가지고있다. 따라서 우리가 Map[K,V]의 동작을 그런 키로 모방하고자한다면 get은 다시 성공해야하지만, 우리가 Bar[List[Int]]을 부여한 이후로는 Bar[List[String]]을 생성해야합니다.

, 우리 DependentMapadd에게 주어진 유형 A하지 않는 한 동일하게 키를 고려하지 않아야 대답이 모든

get에게 주어진 유형 A 동일합니다. 이 경우를 위해 type tags을 사용하는 구현 방법이 있습니다.

import scala.reflect.runtime.universe._ 

class DependentMap[K[_],V[_]](
    inner: Map[ 
    (TypeTag[_], Any), 
    Any 
    ] = Map() 
) { 
    def add[A](
    key: K[A], value: V[A] 
)(
    implicit tt: TypeTag[A] 
): DependentMap[K,V] = { 
    val realKey: (TypeTag[_], Any) = (tt, key) 
    new DependentMap(inner + ((realKey, value))) 
    } 

    def get[A](key: K[A])(implicit tt: TypeTag[A]): Option[V[A]] = { 
    val realKey: (TypeTag[_], Any) = (tt, key) 
    inner.get(realKey).map(_.asInstanceOf[V[A]]) 
    } 
} 

그리고 예상대로 작동하는 몇 가지 예가 있습니다.

scala> val myMap: DependentMap[Foo,Bar] = new DependentMap 


scala> myMap.add(Foo(42), Bar(43)).get(Foo(42)) 
res0: Option[Bar[Int]] = Some(Bar(43)) 

scala> myMap.add(Foo("foo"), Bar("bar")).get(Foo("foo")) 
res1: Option[Bar[String]] = Some(Bar(bar)) 


scala> myMap.add(Foo(42), Bar("bar")).get(Foo(42)) 
error: type mismatch; 

scala> myMap.add[Any](Foo(42), Bar("bar")).get(Foo(42)) 
res2: Option[Bar[Int]] = None 

scala> myMap.add[Any](Foo(42), Bar("bar")).get[Any](Foo(42)) 
res3: Option[Bar[Any]] = Some(Bar(bar)) 


scala> myMap.add(Foo(List[Int]()), Bar(List(43))).get(Foo(List[Int]())) 
res4: Option[Bar[List[Int]]] = Some(Bar(List(43))) 

scala> myMap.add(Foo(List[Int]()), Bar(List(43))).get(Foo(List[String]())) 
res5: Option[Bar[List[String]]] = None 

This works, but the cast is ugly. I'd rather find a better way.

은 그럼 당신은 내 get이 캐스트를 사용하여 구현되는 것을 아마 실망입니다. 다른 방법이 없다는 것을 당신에게 확신 시키려고 노력하겠습니다.

Google 키가 포함되어 있거나 포함되지 않은지도 대신 훨씬 간단한 사례를 생각해 봅시다.

def safeCast[A,B](
    value: A 
)(
    implicit tt1: TypeTag[A], tt2: TypeTag[B] 
): Option[B] = { 
    if (tt1 == tt2) 
    Some(value.asInstanceOf[B]) 
    else 
    None 
} 

는 두 종류의 태그가 동일한 경우, 우리는 A와 B는 같은 종류의 것을 알고, 따라서 배역은 안전, A의 유형 태그와 B에 대한 유형 태그를 감안할 때. 하지만 어떻게 형체 변환없이 이것을 구현할 수 있을까요? 평등 검사는 단순한 부울 값을 반환하지만 some other languages과 같은 평등의 증인이 아닙니다. 따라서 컴파일러는 cast-free 변환이 "true"브랜치에서 합법적이지만 다른 컴파일에서는 불법임을 알 수 없다.

우리의 DependentMap이 더 복잡한 경우에도 add을했을 때 저장 한 태그와 유형 태그를 비교해야하므로 캐스트도 사용해야합니다.

I gather the answer is to use existential types with the forSome keyword

왜 그렇게 생각하는지 알 수 있습니다. 키 (key)와 값 (value)의 쌍이 각각 (Foo[A], Bar[A]) forSome {type A} 인 키 쌍을 원합니다. 당신은 성능과 관련되지 않은 경우 그리고 실제로, 당신은 목록에서 그 연결을 저장할 수 다음 forSome 이후

val myList: List[(Foo[A], Bar[A]) forSome {type A}] 

는 괄호 안에

, 이것은 다른 A를 사용하는 목록의 각 항목을 허용하고 있기 때문에 Foo[A]Bar[A]은 모두 forSome의 왼쪽에 있으며, 각 항목에서 A이 일치해야합니다.

그러나지도의 경우 forSome을 넣어 원하는 결과를 얻을 수있는 곳이 없습니다. Map의 문제점은 두 개의 유형 매개 변수가있어서 forSome을 괄호 밖에 넣지 않고 forSome의 왼쪽에 둘을 넣지 못하게합니다.두 형식 매개 변수가 독립적이기 때문에 왼쪽 형식 매개 변수 중 한 항목과 해당 형식 매개 변수의 해당 항목을 연결하는 것이 없으므로 어떤 형식인지 알 수있는 방법이 없습니다. A 일치해야합니다. 다음 이의 예를 고려 V 값이 있기 때문에 K 값들의 동일한 수가 없다

case class NotMap[K,V](k1: K, k2: K, v1: V, v2: V, v3: V) 

상기 K 값 및 V 값 간의 대응 관계가없는 특정한 너무. Map[{Foo[A], Bar[A]} forSome {type A}]과 같은 특수 구문을 사용하면지도의 각 항목에 대해 A이 일치해야한다는 것을 지정할 수 있습니다. 그런 다음이 구문을 사용하여 말도 안되는 유형 NotMap[{Foo[A], Bar[A]} forSome {type A}]을 지정할 수도 있습니다. 따라서 그러한 구문은 없으므로 DependentMap 또는 HMap과 같이 Map 이외의 유형을 사용해야합니다.