Как элегантно объединить несколько задач, содержащих параметры в ZIO

Я ищу наиболее элегантную реализацию

import scalaz.zio.Task

def combineTasks[A, B, C, D](task1: Task[Option[A]],
                             task2: Task[Option[B]],
                             task3: Task[Option[C]])
                            (f: (A, B, C) => D)
: Task[Option[D]]

с использованием

  1. без дополнительных зависимостей
  2. scalaz-zio-interop-cats и cats
  3. scalaz-zio-interop-scalaz7x и scalaz7x

Решения должны хорошо обобщаться до n аргументов.


person Matthias Langer    schedule 16.03.2019    source источник


Ответы (2)


С кошками вы можете комбинировать несколько вариантов, используя mapN. Итак, это сработает:

import cats._
import cats.implicits._

val o1 = 1.some
val o2 = 2.some
val o3 = 3.some

(o1,o2,o3).mapN((a,b,c) => a |+| b |+| c) // Some(6)

Осталось только развернуть Задачи, и, поскольку они монады, вы можете:

for {
  t1 <- task1 
  t2 <- task2 
  t3 <- task3 
} yield (t1,t2,t3)
  .mapN((a,b,c) => /* combine a,b,c */)
person Krzysztof Atłasik    schedule 16.03.2019
comment
Большое спасибо за ваш ответ. Мне было интересно, смогу ли я сделать что-то вроде Applicative[Task].compose[Option].map3(task1, task2, task3)(f), используя scalaz-zio-interop-cats, но я не мог включить это в компиляцию кода. - person Matthias Langer; 16.03.2019
comment
Это действительно работает - изначально я пытался использовать UIO вместо задачи. Там появляется ошибка Error:(75, 22) diverging implicit expansion for type cats.kernel.Order[A] .... - person Matthias Langer; 16.03.2019
comment
Что это за Onlyc в середине кода? А Олни, наверное, опечатка. - person Andrey Tyukin; 16.03.2019
comment
Извините, я, должно быть, случайно напечатал это. - person Krzysztof Atłasik; 16.03.2019

Получив некоторую помощь и проведя исследование, я нашел следующие реализации, которые пока кажутся мне наиболее элегантными:

1. Без дополнительных зависимостей:

def combineTasks[A, B, C, D](task1: Task[Option[A]],
                             task2: Task[Option[B]],
                             task3: Task[Option[C]])
                            (f: (A, B, C) => D)
: Task[Option[D]] = {
  for {
    t1 <- task1
    t2 <- task2
    t3 <- task3
  } yield {
    (t1, t2, t3) match {
      case (Some(t1), Some(t2), Some(t3)) => Some(f(t1, t2, t3))
      case _ => None
    }
  }
}

2. Использование scalaz-zio-interop-cats и cats:

def combineTasks[A, B, C, D](task1: Task[Option[A]],
                             task2: Task[Option[B]],
                             task3: Task[Option[C]])
                            (f: (A, B, C) => D)
: Task[Option[D]] = {
  import cats.implicits.catsStdInstancesForOption
  import cats.Apply
  import scalaz.zio.interop.catz._

  Apply[Task].compose[Option].map3(task1, task2, task3)(f)
}

См. mapN overposed Apply для соответствующего обсуждения.

3. Использование scalaz-zio-interop-scalaz7x и scalaz7x:

def combineTasks[A, B, C, D](task1: Task[Option[A]],
                             task2: Task[Option[B]],
                             task3: Task[Option[C]])
                            (f: (A, B, C) => D): Task[Option[D]] = {
  import scalaz.Apply
  import scalaz.std.option._
  import scalaz.zio.interop.scalaz72._

  Apply[Task].compose[Option].apply3(task1, task2, task3)(f)
}
person Matthias Langer    schedule 17.03.2019