如何使用组合子嵌入析取的 writer monad 折叠序列?
How to fold a sequence with a writer monad embedding a disjunction using combinators?
我正在进行一些可能会失败的计算,因此我使用了析取类型作为结果。我有一个相当常见的模式,为此我编写了以下函数:
def traverse[A, B, E](a: Iterable[A], b: B)(f: (B, A) => E \/ B): E \/ B =
a.foldLeft[E\/B](\/-(b)){case (b0, a0) => b0 flatMap (f(_, a0))}
但现在我想记录计算(无论是失败还是成功)。我需要一个具有以下类型的函数:
type Logged[T] = scalaz.Writer[String,T]
def traverseLogged[A, B, E]
( a: Iterable[A], lb: Logged[B])
( f: (Logged[B], A) => Logged[E \/ B]): Logged[E \/ B]
但是我找不到 "nice implementation" 依靠组合器。我提出了以下实现:
a.foldLeft[Logged[E \/ B]](lb.map(_.right[E])){
(lb0, a0) =>
val (log, value) = lb0.run
value match {
case -\/(err) => lb0
case \/-(b) => f(b.set(log),a0)
}
}
有没有一种无需 运行 记录的 b 即可实现的方法?
PS:希望问题标题够清楚>_<
首先值得注意的是,您的 traverse
本质上是 foldLeftM
。 foldLeftM
使用 Foldable
实例进行处理,其中不包括 Iterable
,但我建议在使用 Scalaz 时无论如何都要避免使用 Iterable
。
所以你可以写:
a.foldLeftM[({ type L[x] = E \/ x })#L, B](b)(f)
或者:
type MyErrorOr[A] = MyError \/ A
a.foldLeftM[MyErrorOr, B](b)(f)
在 Writer
情况下,我建议不要在累加器中建立 Writer
值。相反,您可以使用 EitherT
monad 转换器:
type StringWriter[A] = Writer[String, A]
type LoggedOr[E, A] = EitherT[StringWriter, E, A]
现在您可以再次使用 foldLeftM
:
def traverseLogged[A, B, E](a: List[A], b: B)(
f: (B, A) => LoggedOr[E, B]
): LoggedOr[E, B] = a.foldLeftM[({ type L[x] = LoggedOr[E, x] })#L, B](b)(f)(
EitherT.eitherTMonad[StringWriter, E]
)
(老实说,我不确定为什么你必须在这里显式地提供 monad 实例——如果我们的 LoggedOr
有一个固定的 [=25] 的类型别名,它就可以正常工作=].)
现在,当您 运行 结果时,您将得到 Writer[String, E \/ B]
,这与原始 traverseLogged
相同。您实际上可以在方法中直接执行 return a Logged
,但如果您需要像这样组合多个操作,则保持在 LoggedOr
级别可能会很有用。
这假定您实际上不需要访问 f
中累积的 Writer
,但如果您这样做,则表明另一种类型可能更合适,无论如何。
我正在进行一些可能会失败的计算,因此我使用了析取类型作为结果。我有一个相当常见的模式,为此我编写了以下函数:
def traverse[A, B, E](a: Iterable[A], b: B)(f: (B, A) => E \/ B): E \/ B =
a.foldLeft[E\/B](\/-(b)){case (b0, a0) => b0 flatMap (f(_, a0))}
但现在我想记录计算(无论是失败还是成功)。我需要一个具有以下类型的函数:
type Logged[T] = scalaz.Writer[String,T]
def traverseLogged[A, B, E]
( a: Iterable[A], lb: Logged[B])
( f: (Logged[B], A) => Logged[E \/ B]): Logged[E \/ B]
但是我找不到 "nice implementation" 依靠组合器。我提出了以下实现:
a.foldLeft[Logged[E \/ B]](lb.map(_.right[E])){
(lb0, a0) =>
val (log, value) = lb0.run
value match {
case -\/(err) => lb0
case \/-(b) => f(b.set(log),a0)
}
}
有没有一种无需 运行 记录的 b 即可实现的方法?
PS:希望问题标题够清楚>_<
首先值得注意的是,您的 traverse
本质上是 foldLeftM
。 foldLeftM
使用 Foldable
实例进行处理,其中不包括 Iterable
,但我建议在使用 Scalaz 时无论如何都要避免使用 Iterable
。
所以你可以写:
a.foldLeftM[({ type L[x] = E \/ x })#L, B](b)(f)
或者:
type MyErrorOr[A] = MyError \/ A
a.foldLeftM[MyErrorOr, B](b)(f)
在 Writer
情况下,我建议不要在累加器中建立 Writer
值。相反,您可以使用 EitherT
monad 转换器:
type StringWriter[A] = Writer[String, A]
type LoggedOr[E, A] = EitherT[StringWriter, E, A]
现在您可以再次使用 foldLeftM
:
def traverseLogged[A, B, E](a: List[A], b: B)(
f: (B, A) => LoggedOr[E, B]
): LoggedOr[E, B] = a.foldLeftM[({ type L[x] = LoggedOr[E, x] })#L, B](b)(f)(
EitherT.eitherTMonad[StringWriter, E]
)
(老实说,我不确定为什么你必须在这里显式地提供 monad 实例——如果我们的 LoggedOr
有一个固定的 [=25] 的类型别名,它就可以正常工作=].)
现在,当您 运行 结果时,您将得到 Writer[String, E \/ B]
,这与原始 traverseLogged
相同。您实际上可以在方法中直接执行 return a Logged
,但如果您需要像这样组合多个操作,则保持在 LoggedOr
级别可能会很有用。
这假定您实际上不需要访问 f
中累积的 Writer
,但如果您这样做,则表明另一种类型可能更合适,无论如何。