Выполнение побочных эффектов с использованием эффекта scala кошек - PullRequest
2 голосов
/ 13 января 2020

Я пытаюсь использовать эффект кошек в scala, и в конце света у меня есть тип: IO[Vector[IO[Vector[IO[Unit]]]]]

Я нашел только один способ запустить его:

for {
    row <- rows.unsafeRunSync()
} yield
 for {
   cell <- row.unsafeRunSync()
 } yield cell.handleErrorWith(errorHandlingFunc).unsafeRunSync()

Но это выглядит довольно некрасиво. Пожалуйста, помогите мне понять, как я могу выполнять сложные побочные эффекты.

ОБНОВЛЕНИЕ:

1) Сначала IO - я открываю файл Excel и получаю вектор строк, то есть IO[Vector[Row]].

2) Второй IO - я выполняю запрос к БД для каждой строки. Я не могу составить монаду ввода-вывода с Vector[_],

3) Третий IO - я создаю файл PDF для каждой строки из Excel, используя Vector[Results] из БД.

Так что у меня есть такие функции как:

1) String=>IO[Vector[Row]]

2) Row=>IO[Vector[Results]]

3) Vector[Results] => IO[Unit]

1 Ответ

3 голосов
/ 13 января 2020

В качестве примера приведем бессмысленное действие, которое я только что изобразил на макушке того же типа:

import cats.effect.IO

val actions: IO[Vector[IO[Vector[IO[Unit]]]]] =
  IO(readLine).flatMap(in => IO(in.toInt)).map { count =>
    (0 until count).toVector.map { _ =>
      IO(System.nanoTime).map { t =>
        (0 until 2).toVector.map { _ =>
          IO(println(t.toString))
        }
      }
    }
  }

Здесь мы читаем строку из стандартного ввода, анализируем это целое число, смотрящее на текущее время столько раз и печатающее его дважды каждый раз.

Правильный способ выравнивания этого типа - использовать sequence для переупорядочения слоев:

import cats.implicits._

val program = actions.flatMap(_.sequence).flatMap(_.flatten.sequence_)

(или что-то подобное - есть много разумных способов написать это.)

Эта программа имеет тип IO[Unit] и работает так, как мы ожидали:

scala> program.unsafeRunSync
// I typed "3" here
8058983807657
8058983807657
8058984254443
8058984254443
8058984270434
8058984270434

Каждый раз, когда вы видите глубоко вложенный тип, включающий несколько слоев IO и подобные коллекции, вероятно, лучше всего избегать попадания в эту ситуацию в первую очередь (обычно с помощью * 1017). *). В этом случае мы могли бы переписать наш оригинальный actions следующим образом:

val actions: IO[Unit] =
  IO(readLine).flatMap(in => IO(in.toInt)).flatMap { count =>
    (0 until count).toVector.traverse_ { _ =>
      IO(System.nanoTime).flatMap { t =>
        (0 until 2).toVector.traverse { _ =>
          IO(println(t.toString))
        }
      }
    }
  }

Это будет работать точно так же, как наши program, но мы избежали вложения, заменив map s в нашем оригинальном actions с flatMap или traverse. Знание того, что вам нужно, где вы чему-то научитесь на практике, но когда вы начинаете, лучше всего набрать go наименьшим возможным шагом и следовать типам.

...