How to call a function which uses MTL and Parallel?

早过忘川 提交于 2019-12-08 07:51:18

问题


I am using Parallel type class in order to collect all validation errors:

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)

The getUser function has two type parameters:

  • M is my monad transformer stack,
  • F is some applicative which is dual to M but allows parallel execution.

Then I want to call it with the following monad transformer stack:

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

I need to specify the M type parameter to tell the compiler which stack I am using. But then I have to specify the F parameter as well:

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

This looks pretty ugly. Is there any way to let compiler infer F?

Full code is here: https://gist.github.com/vkorenev/21bdd7d57e81a0752972f4bb3f45398a


回答1:


Try "partial application"

  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)

Or create a type class

  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)



回答2:


It is possible to either use cats-par library or to add an auxiliary Parallel1 type class as suggested here.

Then getUser will need only one type parameter:

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)
}

Hopefully, one of the fixes will be added to cats.



来源:https://stackoverflow.com/questions/55235849/how-to-call-a-function-which-uses-mtl-and-parallel

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!