Я не думаю, что то, что вы ищете, возможно в принципе.
Хороший синтаксис for
, который вы ищете, переводит что-то вроде
f0.flatMap(v0 => (f1.map(v1 => f0f1(v0, v1))))
Проблема здесь заключается вflatMap
часть.Чтобы быть flatMap
, вам нужен Monad
, а не только Functor
или хотя бы что-то с экземпляром FlatMap
.Но Monad
с, в отличие от Functor
с, не сочиняют.Таким образом, невозможно получить вложенные Monad
автоматически из F[_]
и G[_]
, даже если оба они Monad
s.[1]
Вы можете получить хороший синтаксис, используя некоторые монадные преобразователи, но они не существуют (и не могут существовать) для всех монад.[2]
Для монад, где есть трансформаторы, возможно что-то вроде того, что вы хотите:
val l: List[Int] = List(1,2,3)
val o: Option[String] = Some("abc")
val ol: OptionT[List, String] = for {
a <- OptionT.liftF(l)
b <- OptionT.fromOption[List](o)
} yield a + b.toString
Есть scala docs ([3]), если вы заинтересованы в OptionT
.
Неважно, приятнее это или нет, лежит в глазах смотрящего.
Как это ни печально, если вам это нужно чаще, вы можете написать свои собственные вспомогательные функции в духе
def combine2[F[_]: Functor, G[_]: Functor, A, B, C](
fa: F[A],
gb: G[B])(
f: (A, B) => C
): F[G[C]] =
fa.map(a => gb.map(b => (f(a, b))))
Если вы не хотите этого делать, одну вещь, которую вы можете сделать, - это то, что уже упоминал @ andrey-tyukin.Но я согласен, что, вероятно, лучше всего вкладывать вызовы в map
.
Еще одна вещь, на которую вы, возможно, захотите взглянуть, это Nested
[4].Это не поможет вам в данном конкретном случае, но может помочь вам уменьшить количество вложенных map
s в будущем.
-
[1] http://blog.tmorris.net/posts/monads-do-not-compose/
[2] Почему в Хаскеле нет трансформатора ввода-вывода?
[3] https://typelevel.org/cats/api/cats/data/OptionT.html
[4] https://typelevel.org/cats/api/cats/data/Nested.html