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))?
이 시간 fooListInt
및 fooListStr
보기가 별개해야하지만, 사실 둘 다 List[Int]
및 List[String]
모두의 하위 유형 인 유형 List[Nothing]
,의 값 Nil
을 가지고있다. 따라서 우리가 Map[K,V]
의 동작을 그런 키로 모방하고자한다면 get
은 다시 성공해야하지만, 우리가 Bar[List[Int]]
을 부여한 이후로는 Bar[List[String]]
을 생성해야합니다.
, 우리 DependentMap
이 add
에게 주어진 유형 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
이외의 유형을 사용해야합니다.
감사합니다 ... 그 후에 내가 무엇을 달성 할 수있는 아이디어가 있습니까? –
@Marcus Downing : 편집 –
보기.보기 흉하게 들립니다. 실제로 FooBarPair를 만들지 않고 그냥 파생 된 유형을 사용합니까? –