Scalaz 7:将Either中的值转换为普通值加上记录错误的惯用法?

时间:2012-09-16 15:05:56

标签: logging scalaz scalaz7

给定一个函数f: A => E \/ B,在记录B s的同时收集AE列表的import scalaz._, Scalaz._ type Logger[+A] = Writer[List[String], A] def logged[A, E, B, F[_]](f: A => E \/ B)(implicit FM: Monoid[F[E]], FP: Pointed[F]): (A => Writer[F[E], Option[B]]) = (a: A) => f(a).fold(e => Writer(FP.point(e), None), b => Writer(FM.zero, Some(b))) def listLogged[A, E, B](f: A => E \/ B) = logged[A, E, B, List](f) type W[+A] = Writer[List[String], A] def keepOdd(n: Int): String \/ Int = if (n % 2 == 1) \/.right(n) else \/.left(n + " was even") scala> val x = List(5, 6).traverse[W, Option[Int]](listLogged(keepOdd)) x: W[List[Option[Int]]] = scalaz.WriterTFunctions$$anon$26@503d0400 scala> x.run res11: (List[String], List[Option[Int]]) = (List(6 was even),List(Some(5), None)) scala> val (logs, results) = x.map(_.flatten).run logs: List[String] = List(6 was even) results: List[Int] = List(5) 结果的惯用方法是什么?

我想出了以下内容(部分在answering this SO question期间):

{{1}}

是否有更短/更好/更少限制/更一般的方式?

1 个答案:

答案 0 :(得分:2)

您可以使用putWith来描述我的眼睛是一种更具可读性的logged方法:

def logged[A, E, B, F[_]: PlusEmpty: Pointed](f: A => E \/ B) = (a: A) =>
  WriterT.putWith(f(a).point[Id])(_.swap.toOption.orEmpty[F]).map(_.toOption)

我还认为PlusEmpty上绑定的F上下文看起来比明确要求F[E]]成为一个幺半群更清晰(当然,它完成同样的事情)。遗憾的是,.point[Id]位是必要的 - 没有Writer.putWith - 但乞丐不能选择,我猜。

我也会这样写keepOdd

def keepOdd(n: Int) = Either.cond(n % 2 == 1, n, n + " was even").disjunction

或者至少使用n.right代替\/.right(n),但这仅仅是品味问题。