이해를 위해 Scala의 유형 불일치


81

이 구조로 인해 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)

2
실패한 예제에서 Scala가 어떤 결과를 반환 할 것으로 예상 했습니까?
Daniel C. Sobral 2011 년

1
내가 그것을 쓸 때 나는 Option [List [(Int, Int)]]를 얻을 것이라고 생각했습니다.
Felipe Kamakura

답변:


117

이해를 위해 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있습니다.


31

기억하기 쉬운 팁 은 이해 를 위해 첫 번째 생성기의 컬렉션 유형 인 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 로의 암시 적 변환이 있습니다.

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


for이런 유형의 functor / monadic desugaring 을 정규 구문 으로 만드는 것은 나쁜 디자인 선택이라고 주장하고 싶습니다 . 같은 functor / monad 매핑에 대해 다른 이름의 메서드를 사용 하고 거의 다른 주류 프로그래밍 언어에서 오는 기대치와 일치하는 매우 간단한 동작을 갖도록 구문을 fmap예약 하지 않는 이유는 무엇 for입니까?
ely

메인 스트림 순차 컴퓨팅 제어 흐름 문이 매우 놀랍고 미묘한 성능 문제 등을 가지지 않고도 원하는대로 별도의 fmap / lift 종류를 일반적인 것으로 만들 수 있습니다. "Everything works with for"는 그만한 가치가 없습니다.
ely

4

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


1

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

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)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.