Поведение вариантов внутри для понимания является Scala

Два вопроса новичка.

Кажется, что for понимание знает о Options и может автоматически пропустить None и развернуть Some например,

val x = Map("a" -> List(1,2,3), "b" -> List(4,5,6), "c" -> List(7,8,9))
val r = for {map_key <- List("WRONG_KEY", "a", "b", "c")
             map_value <- x get map_key } yield map_value

выходы:

r: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))

Где сделать Options идти? Может кто-нибудь, пожалуйста, пролить свет на то, как это работает? Можем ли мы всегда полагаться на это поведение?

Второе, почему это не компилируется?

val x = Map("a" -> List(1,2,3), "b" -> List(4,5,6), "c" -> List(7,8,9))
val r = for {map_key <- List("WRONG_KEY", "a", "b", "c")
                 map_value <- x get map_key
                 list_value <- map_value
    } yield list_value

Это дает

Error:(57, 26) type mismatch;
 found   : List[Int]
 required: Option[?]
             list_value <- map_value
                        ^

Глядя на тип первого примера, я не уверен, почему нам нужно иметь Option Вот?

4 ответа

Может быть, эта небольшая разница, установка скобок и вызов flatten, проясняет:

val r = for {map_key <- List("WRONG_KEY", "a", "b", "c")
     |             } yield x get map_key 

r: List[Option[List[Int]]] = List(None, Some(List(1, 2, 3)), Some(List(4, 5, 6)), Some(List(7, 8, 9)))

val r = (for {map_key <- List("WRONG_KEY", "a", "b", "c")
     |             } yield x get map_key).flatten 

r: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))

Это эквивалентно:

scala> List("WRONG_KEY", "a", "b", "c").map (x get _)
res81: List[Option[List[Int]]] = List(None, Some(List(1, 2, 3)), Some(List(4, 5, 6)), Some(List(7, 8, 9)))

scala> List("WRONG_KEY", "a", "b", "c").map (x get _).flatten
res82: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))

Промежуточное значение (map_key) исчезло как _ во втором блоке.

Вы смешиваете две разные монады (List а также Option) внутри для заявления. Это иногда работает, как ожидалось, но не всегда. В любом случае вы можете самостоятельно преобразовывать параметры в списки:

for {
  map_key <- List("WRONG_KEY", "a", "b", "c")
  list_value <- x get map_key getOrElse Nil 
} yield list_value

Я думаю, что разница заключается в том, что понимание разбирается в вызовах метода map() и flatMap внутри черты Seq.

Для краткости давайте определим некоторые переменные:

var keys = List("WRONG_KEY", a, b, c)

Ваш первый случай эквивалентен:

val r = keys.flatMap(x.get(_))

тогда как ваш второй случай эквивалентен:

val r= keys.flatMap(x.get(_).flatMap{ case y => y })

Я думаю, что проблема в том, что Option.flatMap() должен возвращать Option[], что хорошо в первом случае, но не согласуется во втором случае с тем, что передается x.get(). FlatMap, который является Список [Int].

Эти правила перевода для понимания более подробно объясняются в главе 7 "Программирование Scala" Wampler & Payne.

Для понимания преобразуются в вызовы в последовательность вызовов карты или flatMap. Смотрите здесь

Ваш for цикл эквивалентен

List("WRONG_KEY", "a", "b", "c").flatMap(
  map_key => x.get(map_key).flatMap(map_value => map_value)
)

flatMap в Option определяется как

 @inline final def flatMap[B](f: A => Option[B]): Option[B]

Так что не разрешено проходить List в качестве аргумента, как вы уведомлены компилятором.

Другие вопросы по тегам