mapN перекомпонован Применить

Я знаю, что могу составить Apply для работы с вложенными структурами, как в

def mapAll[A, B, C, D](o1: List[Option[A]],
                       o2: List[Option[B]],
                       o3: List[Option[C]])
                      (f: (A, B, C) => D)
: List[Option[D]] = {
  import cats._
  import cats.implicits._
  Apply[List].compose[Option].map3(o1, o2, o3)(f)
}

Однако есть ли способ убедить компилятор принять (o1, o2, o3).mapN(f) вместо Apply[List].compose[Option].map3(o1, o2, o3)(f), чтобы mapN применялся с использованием составного Apply?


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


Ответы (1)


Это именно то, для чего cats.data.Nested:

def mapAll[A, B, C, D](o1: List[Option[A]],
                       o2: List[Option[B]],
                       o3: List[Option[C]])
                      (f: (A, B, C) => D)
: List[Option[D]] = {
  import cats.data.Nested
  import cats.instances.list._, cats.instances.option._
  import cats.syntax.apply._

  (Nested(o1), Nested(o2), Nested(o3)).mapN(f).value
}

(Обратите внимание, что вам нужно будет включить флаг компилятора -Ypartial-unification для компиляции приведенного выше кода. В качестве альтернативы вы можете добавить некоторые параметры типа, но, в первую очередь, я не уверен, где именно они потребуются, и если вы в любом случае делать что-нибудь с кошками -Ypartial-unification в значительной степени необходимо.)

Вы также можете просто сделать составной экземпляр доступным неявно:

import cats.Apply
import cats.instances.list._, cats.instances.option._
import cats.syntax.apply._

type ListOption[x] = List[Option[x]]

implicit val listOptionApply: Apply[ListOption] = Apply[List].compose[Option]

def mapAll[A, B, C, D](o1: ListOption[A],
                       o2: ListOption[B],
                       o3: ListOption[C])
                      (f: (A, B, C) => D)
: List[Option[D]] = (o1, o2, o3).mapN(f)

Однако это действительно не идеально - это нестандартно и чрезвычайно хрупко (например, псевдонимы типов необходимы для определения разрешения).

На мой взгляд, лучше всего просто написать Apply[List].compose[Option] явно и пропустить причудливый синтаксис кортежа, но если вам действительно нужен необычный синтаксис кортежа, используйте Nested.

person Travis Brown    schedule 16.03.2019
comment
Большое спасибо за ваш ответ! Я не могу скомпилировать первый фрагмент: Error:(23, 42) value mapN is not a member of (cats.data.Nested[List,Option,A], cats.data.Nested[List,Option,B], cats.data.Nested[List,Option,C]) (Nested(o1), Nested(o2), Nested(o3)).mapN(f).value (я использую scala-2.12.8 с cats-1.6.0) - person Matthias Langer; 17.03.2019
comment
Вы изменили импорт или у вас есть другие виды импорта? - person Travis Brown; 17.03.2019
comment
Я не могу скомпилировать scalafiddle.io/sf/31wU12L/1 локально с scala-2.12.8 и cats-1.6.0 или cats-1.3.1 ( как используется scalafiddle). Я удалил все плагины компилятора и параметры компилятора из моего файла сборки. Интересно, что scalafiddle компилирует фрагмент ... - person Matthias Langer; 17.03.2019
comment
@MatthiasLanger О, тебе нужен -Ypartial-unification. Я отредактировал ответ, чтобы отметить это. - person Travis Brown; 17.03.2019