Почему это for-comprehension, начиная с Option, не компилируется в Scala?

Учитывая эти определения:

val guys = List("Albert", "Tom")
val girls = List("Mary", "Stacy", "Josie")

val optRoom: Option[String] = Some("Room 303")

Это оценивает для меня ожидаемое List[String]:

for {
  guy <- guys
  girl <- girls
  room <- optRoom
} yield {
  s"$guy dances with $girl in $room"
}

Немного повернув вещи внутри цикла, я бы ожидал, что это будет оцениваться как Option[List[String]], но оно даже не компилируется:

for {
  room <- optRoom
  guy <- guys
  girl <- girls
} yield {
  s"$guy dances with $girl in $room"
}  

Почему нет? Что мне не хватает? (Я знаю, что могу преобразовать optRoot в последовательность/список/итерацию, но это не то, что я хочу)

Как мне переписать свой код, чтобы получить Option[List[String]]?


person Sebastian N.    schedule 22.05.2014    source источник


Ответы (2)


Ваш второй пример эквивалентен

optRoom flatMap { room =>
  guys flatMap { guy =>
    girls map { girl =>
      s"$guy dances with $girl in $room"
    }
  }
}

Что не работает, потому что вы не возвращаете Option в optRoom.flatMap.

Если вам нужен Option[List[String]], то вам нужно вызывать optRoom.map, а не flatMap, потому что вы хотите, чтобы результат был заключен в Option:

for {
  room <- optRoom
} yield for {
  guy <- guys
  girl <- girls
} yield {
  s"$guy dances with $girl in $room"
}
person Dan Getz    schedule 22.05.2014
comment
Спасибо за ответ. Вы говорите: потому что вы не возвращаете Option в optRoom.flatMap. Возможно ли это сделать? Будет ли это иметь смысл? - person Sebastian N.; 22.05.2014
comment
Было бы разумно, если бы результат, который вы вычисляете в случае, когда optRoom не равен None, естественно, является Option[something]. В вашем случае это, естественно, List[String]. Таким образом, вы можете превратить это в Some[List[String]], но это просто использование flatMap для эмуляции map. - person Dan Getz; 22.05.2014

Ты можешь сделать:

optRoom.map(room => for {
  guy <- guys
  girl <- girls
} yield s"$guy dances with $girl in $room")

Ваш второй for примерно переводится как:

optRoom.flatMap(room => guys.flatMap(guy => girls.map(girl => s"$guy dances with $girl in $room")))

Поскольку у вас есть optRoom.flatMap(room => ...), ожидается, что параметр является функцией из String => Option[B], но guys.flatMap(...) не имеет типа Option.

person Kigyo    schedule 22.05.2014