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
呼叫站点的确切类型(因为它被硬编码为Task
、IO
、Coeval
等),您可以直接使用恢复方法。
主要区别在于结果F[Either[E, A]]
不会告诉调用者E
应该被视为失败。F[A]
告诉我们只有A
成功的价值。此外,一个需要Applicative
while 另一个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 它主要是关于表达意图,因为两者在“道德上”是平等的。