[scala] 이해를 위해 Scala의 유형 불일치

이 구조로 인해 Scala에서 유형 불일치 오류가 발생하는 이유는 무엇입니까?

for (first <- Some(1); second <- List(1,2,3)) yield (first,second)

<console>:6: error: type mismatch;
 found   : List[(Int, Int)]
 required: Option[?]
       for (first <- Some(1); second <- List(1,2,3)) yield (first,second)

Some을 List로 전환하면 잘 컴파일됩니다.

for (first <- List(1,2,3); second <- Some(1)) yield (first,second)
res41: List[(Int, Int)] = List((1,1), (2,1), (3,1))

이것은 또한 잘 작동합니다.

for (first <- Some(1); second <- Some(2)) yield (first,second)



답변

이해를 위해 map또는 flatMap메서드 에 대한 호출로 변환됩니다 . 예를 들면 다음과 같습니다.

for(x <- List(1) ; y <- List(1,2,3)) yield (x,y)

됩니다 :

List(1).flatMap(x => List(1,2,3).map(y => (x,y)))

따라서 첫 번째 루프 값 (이 경우 List(1))은 flatMap메서드 호출 을 수신합니다 . flatMapon a List가 다른 것을 반환 하기 때문에 Listfor comprehension의 결과는 물론 List. (이것은 나에게 새로운 것이 었습니다 Seq.

이제 다음에서 어떻게 flatMap선언 되는지 살펴보십시오 Option.

def flatMap [B] (f: (A) ⇒ Option[B]) : Option[B]

이것을 명심하십시오. 이해에 대한 오류 (가있는 오류 Some(1))가 일련의 맵 호출로 변환되는 방법을 살펴 보겠습니다 .

Some(1).flatMap(x => List(1,2,3).map(y => (x, y)))

이제 flatMap호출 의 매개 변수가 필요에 따라 를 반환하지만는 반환 List하지 않는 것임을 쉽게 알 수 있습니다 Option.

문제를 해결하기 위해 다음을 수행 할 수 있습니다.

for(x <- Some(1).toSeq ; y <- List(1,2,3)) yield (x, y)

잘 컴파일됩니다. 종종 가정 하듯이 Option의 하위 유형이 아니라는 점은 주목할 가치가 Seq있습니다.


답변

기억하기 쉬운 팁 은 이해 를 위해 첫 번째 생성기의 컬렉션 유형 인 Option [Int]를 반환하려고합니다. 따라서 Some (1)으로 시작 하면 Option [T]의 결과를 예상해야합니다.

목록 유형 의 결과를 원하면 목록 생성기로 시작해야합니다.

왜이 제한이 있고 항상 일종의 시퀀스를 원할 것이라고 가정하지 않습니까? 돌아 오는 것이 합리적 일 수 있습니다 Option. 다음 함수를 사용하여 Option[Int]를 얻기 위해 무언가와 결합하려는를 가질 수 있습니다 Option[List[Int]]. (i:Int) => if (i > 0) List.range(0, i) else None; 그런 다음 이것을 작성하고 “말이 안되는”경우 None을 얻을 수 있습니다.

val f = (i:Int) => if (i > 0) Some(List.range(0, i)) else None
for (i <- Some(5); j <- f(i)) yield j
// returns: Option[List[Int]] = Some(List(0, 1, 2, 3, 4))
for (i <- None; j <- f(i)) yield j
// returns: Option[List[Int]] = None
for (i <- Some(-3); j <- f(i)) yield j
// returns:  Option[List[Int]] = None

일반적인 경우에 대한 이해 를 확장 하는 방법 은 실제로 유형의 객체를 M[T]함수와 결합하여 유형 (T) => M[U]의 객체를 얻는 상당히 일반적인 메커니즘 M[U]입니다. 귀하의 예에서 M은 옵션 또는 목록이 될 수 있습니다. 일반적으로 동일한 유형이어야합니다 M. 따라서 Option과 List를 결합 할 수 없습니다. 다른 것들의 예를 들어 M, 이 특성의 하위 클래스를 보십시오 .

왜 결합 않은 List[T]으로 (T) => Option[T]당신이 목록 시작 때 비록 사용할 수 있습니까? 이 경우 라이브러리는 더 일반적인 유형을 사용합니다. 따라서 List와 Traversable을 결합 할 수 있으며 Option에서 Traversable 로의 암시 적 변환이 있습니다.

결론은 이것입니다. 표현식이 반환 할 유형을 생각하고 해당 유형을 첫 번째 생성자로 시작합니다. 필요한 경우 해당 유형으로 포장하십시오.


답변

Option이 Iterable이 아닌 것과 관련이있을 수 있습니다. 암시 Option.option2Iterable적은 컴파일러가 두 번째가 Iterable 일 것으로 예상하는 경우를 처리합니다. 루프 변수의 유형에 따라 컴파일러 마법이 다를 것으로 예상합니다.


답변

나는 항상 이것이 도움이된다는 것을 알았다.

scala> val foo: Option[Seq[Int]] = Some(Seq(1, 2, 3, 4, 5))
foo: Option[Seq[Int]] = Some(List(1, 2, 3, 4, 5))

scala> foo.flatten
<console>:13: error: Cannot prove that Seq[Int] <:< Option[B].
   foo.flatten
       ^

scala> val bar: Seq[Seq[Int]] = Seq(Seq(1, 2, 3, 4, 5))
bar: Seq[Seq[Int]] = List(List(1, 2, 3, 4, 5))

scala> bar.flatten
res1: Seq[Int] = List(1, 2, 3, 4, 5)

scala> foo.toSeq.flatten
res2: Seq[Int] = List(1, 2, 3, 4, 5)


답변