4

There is ApplicativeError[F,E] + F[A] and there is Either[E, A]. Both convey the message that the function could fail with an E or succeed with an A but I'm not sure about the different message they convey to the client about the intended way of handling the error :

def f1[F[_]: Applicative[F]]: F[Either[E, A]]
def f2[F[_]: ApplicativeError[F,E]]: F[A]

I understand f1 means: client is responsible for error handling. But what does f2 mean to the client about how to handle the error ?

4

1 回答 1

6

ApplicativeError[F, E]假设 of 的类型以E某种方式被编码,F并且您可以创建实例 of ApplicativeError[F, E]only 因为对于这个特定F的情况,有 error 是有意义的E。例子:

  • ApplicativeError[Task, Throwable]-Task用作错误通道,因此暴露为错误代数Throwable是有意义的。Throwable事实上Sync[F],Cats Effect implementsMonadError[F, Throwable]反过来实现ApplicativeError[F, Throwable]了 Cats Effect 的许多类型类假设您只处理Throwable
  • ApplicativeError[Task[Either[E, *]], E]- 在这样的组合中,您将E同时拥有 typeF的特定定义和E参数 - 这对于所有类型的双函子都是典型的:Task[Either[E, *]], EitherT[Task, E, *], ZIO'sIO[E, A]等等

该接口ApplicativeError[F, E]不自行处理错误。但它公开了方法:

  • raiseError[A](e: E): F[A]- 这会产生错误
  • handleErrorWith[A](fa: F[A])(f: (E) ⇒ F[A]): F[A]- 处理它

这样你就可以告诉它如何处理它。

两者都可以在不假设错误的性质和 F 其他Applicative可能失败的情况下工作。如果您只使用F和键入类,则可以使用这些方法从错误中恢复。如果您知道F呼叫站点的确切类型(因为它被硬编码为TaskIOCoeval等),您可以直接使用恢复方法。

主要区别在于结果F[Either[E, A]]不会告诉调用者E应该被视为失败。F[A]告诉我们只有A成功的价值。此外,一个需要Applicativewhile 另一个ApplicativeError,因此创建值所需的“功率”有所不同 - 如果您看到ApplicativeError即使没有E结果,您也可以假设该方法可能会失败,因为它需要更强大的类型类。

但当然它不是一成不变的,它主要是关于表达意图,因为无论你在哪里,你都F[A]可以转换为F[Either[E, A]]使用ApplicativeError[F, E](甚至还有像attempt[A](fa: F[A]): F[Either[E, A]]or之类的方法fromEither[A](x: Either[E, A]): F[A])。因此,在您的应用程序的一部分中,您可以F[A]使用E代数,但是您可以使用一个纯函数来使用Either[E, A] => B它——然后您可以转换、映射,并在必要时转换回来。

TL; DR 它主要是关于表达意图,因为两者在“道德上”是平等的。

于 2020-04-28T15:21:32.380 回答