2

関数が与えられた場合、 sをログに記録しながら、 sのリストの結果f: A => E \/ Bを収集する慣用的な方法は何ですか?BAE

私は次のことを思いついた(部分的にこのSOの質問に答えている間):

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)

より短い/より良い/より制限の少ない/より一般的な方法はありますか?

4

1 に答える 1

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)、それは好みの問題です。

于 2012-09-17T23:00:57.473 に答える