Как вызвать функцию, которая использует MTL и Parallel? - PullRequest
1 голос
/ 19 марта 2019

Я использую класс Parallel для сбора всех ошибок проверки:

def getNonEmptyStr[M[_]](key: String)(
    implicit
    E: MonadError[M, Errors],
    A: ApplicativeAsk[M, Params],
    W: FunctorTell[M, List[String]]
): M[String] = ???

def getInt[M[_]](key: String)(
    implicit
    E: MonadError[M, Errors],
    A: ApplicativeAsk[M, Params],
    W: FunctorTell[M, List[String]]
): M[Int] = ???

def getUser[M[_], F[_]](
    implicit E: MonadError[M, Errors],
    R: ApplicativeAsk[M, Params],
    W: FunctorTell[M, List[String]],
    P: Parallel[M, F]
): M[User] = 
    (getNonEmptyStr("name"), getInt("age"), getNonEmptyStr("address"))
        .parMapN(User)

Функция getUser имеет два параметра типа:

  • Mэто мой стек монадных преобразователей,
  • F - это некоторое аппликативное значение, двойственное к M, но допускающее параллельное выполнение.

Затем я хочу вызвать его со следующим стеком монадного преобразователя:

type Stack[A] = EitherT[WriterT[Reader[Params, ?], List[String], ?], Errors, A]

Мне нужно указать параметр типа M, чтобы сообщить компилятору, какой стек я использую,Но тогда я должен указать и параметр F:

getUser[Stack, Nested[WriterT[Reader[Params, ?], List[String], ?], Validated[Errors, ?], ?]].value.run.run(params)

Это выглядит довольно уродливо.Есть ли способ позволить компилятору выводить F?

Полный код здесь: https://gist.github.com/vkorenev/21bdd7d57e81a0752972f4bb3f45398a

Ответы [ 2 ]

2 голосов
/ 19 марта 2019

Попробуйте "частичное применение"

  def getUser[M[_]](implicit E: MonadError[M, Errors],
                    R: ApplicativeAsk[M, Params],
                    W: FunctorTell[M, List[String]]
                   ) = new GetUserHlp[M]

  class GetUserHlp[M[_]](implicit E: MonadError[M, Errors],
                         R: ApplicativeAsk[M, Params],
                         W: FunctorTell[M, List[String]]
                        ) {
    def apply[F[_]](implicit P: Parallel[M, F]): M[User] =
      (getNonEmptyStr("name"), getInt("age"), getNonEmptyStr("address"))
        .parMapN(User)
  }

  getUser[Stack].apply.value.run.run(params)

Или создайте класс типов

  trait GetUser[M[_]] {
    def apply(): M[User]
  }

  object GetUser {
    implicit def default[M[_], F[_]](implicit E: MonadError[M, Errors],
                                     R: ApplicativeAsk[M, Params],
                                     W: FunctorTell[M, List[String]],
                                     P: Parallel[M, F]
                                    ): GetUser[M] = new GetUser[M] {
      override def apply(): M[User] = (getNonEmptyStr("name"), getInt("age"), getNonEmptyStr("address"))
        .parMapN(User)
    }
  }

  def getUser[M[_]](implicit gu: GetUser[M]): M[User] = gu()

  getUser[Stack].value.run.run(params)
0 голосов
/ 25 марта 2019

Можно использовать библиотеку cats-par или добавить вспомогательный класс типа Parallel1, как предлагается здесь .

Тогда getUser будетнужен только один параметр типа:

def getUser[M[_]](
    implicit
    E: MonadError[M, Errors],
    R: ApplicativeAsk[M, Params],
    W: FunctorTell[M, List[String]],
    P: Parallel1[M]
): M[User] = {
  import P._
  (getNonEmptyStr("name"), getInt("age"), getNonEmptyStr("address")).parMapN(User)
}

Надеемся, что одно из исправлений будет добавлено в cats.

...