22

ロギングにscalaz.WriterTをどのように使用しますか?

4

2 に答える 2

34

モナド変換子について

これは非常に短い紹介です。詳細については、haskellwikiまたは@jrwestによるこのすばらしいスライドをご覧ください

モナドは構成されません。つまり、モナドA[_]とモナドがある場合B[_]自動的A[B[_]]に導出することはできません。ただし、ほとんどの場合、これは、特定のモナドに対していわゆるモナド変換子を使用することで実現できます。

BTモナド用のモナド変換子がある場合は、任意のモナドBに新しいモナドを作成できます。そうです、を使って中に入れることができます。A[B[_]] ABTBA

scalazでのモナド変換子の使用

率直に言って、私はscalaz 6でモナド変換子を使用しなかったので、以下はscalaz7を想定しています

モナド変換子MTは2つの型パラメーターを取ります。1つはラッパー(外部)モナドで、2つ目はモナドスタックの最下部にある実際のデータ型です。注:より多くのタイプパラメーターが必要になる場合がありますが、それらはトランスフォーマーネスとは関係ありませんが、特定のモナドに固有です(ログに記録されたタイプのWriter、またはエラータイプのValidation)。

したがってList[Option[A]]、単一の構成されたモナドとして扱いたいものがある場合は、が必要OptionT[List, A]です。ある場合はOption[List[A]]、が必要ListT[Option, A]です。

そこに着く方法?トランスフォーマー以外の値がある場合は、通常、それをラップしMT.applyてトランスフォーマー内の値を取得できます。変換されたフォームから通常の形式に戻すには、通常、.run変換された値を呼び出します。

つまりval a: OptionT[List, Int] = OptionT[List, Int](List(some(1))、とval b: List[Option[Int]] = a.runは同じデータであり、表現だけが異なります。

Tony Morrisは、変換されたバージョンにできるだけ早く入り、それをできるだけ長く使用するのが最善であると提案しました。

注:トランスフォーマーを使用して複数のモナドを構成すると、通常のデータ型とは正反対のタイプのトランスフォーマースタックが生成されます。したがって、通常List[Option[Validation[E, A]]]は次のようになりますtype ListOptionValidation[+E, +A] = ValidationT[({type l[+a] = OptionT[List, a]})#l, E, A]

更新:scalaz 7.0.0-M2の時点でValidationは、(正しく)モナドではないため、ValidationT存在しません。EitherT代わりに使用してください。

ロギングにWriterTを使用する

必要に応じて、WriterT特定の外部モナドなしで使用するか(この場合、バックグラウンドでId何もしないモナドを使用します)、ログをモナド内に配置するか、モナドをログ内に配置することができます。

最初のケース、単純なロギング

import scalaz.{Writer}
import scalaz.std.list.listMonoid
import scalaz._

def calc1 = Writer(List("doing calc"), 11)
def calc2 = Writer(List("doing other"), 22)

val r = for {
  a <- calc1
  b <- calc2
} yield {
  a + b
}

r.run should be_== (List("doing calc", "doing other"), 33)

listMonoidインスタンスも提供するため、インスタンスをインポートしSemigroup[List]ます。WriterTログ値を組み合わせることができるようにするには、ログタイプがセミグループである必要があるため、これが必要です。

2番目のケース、モナド内でのロギング

ここでは、Option簡単にするためにモナドを選択しました。

import scalaz.{Writer, WriterT}
import scalaz.std.list.listMonoid
import scalaz.std.option.optionInstance
import scalaz.syntax.pointed._

def calc1 = WriterT((List("doing calc") -> 11).point[Option])
def calc2 = WriterT((List("doing other") -> 22).point[Option])

val r = for {
  a <- calc1
  b <- calc2
} yield {
  a + b
}

r.run should be_== (Some(List("doing calc", "doing other"), 33))

このアプローチでは、ロギングはOptionモナド内にあるため、バインドされたオプションのいずれかがである場合、ログなしNoneで結果が得られます。None

注:x.point[Option]実質的にはと同じですがSome(x)、コードをより一般化するのに役立つ場合があります。今のところ、致命的ではありません。

3番目のオプション、モナドの外でログを記録する

import scalaz.{Writer, OptionT}
import scalaz.std.list.listMonoid
import scalaz.std.option.optionInstance
import scalaz.syntax.pointed._

type Logger[+A] = WriterT[scalaz.Id.Id, List[String], A]

def calc1 = OptionT[Logger, Int](Writer(List("doing calc"), Some(11): Option[Int]))
def calc2 = OptionT[Logger, Int](Writer(List("doing other"), None: Option[Int]))

val r = for {
  a <- calc1
  b <- calc2
} yield {
  a + b
}

r.run.run should be_== (List("doing calc", "doing other") -> None)

ここでは、モナドを内OptionTに配置するために使用します。計算の1つは、この場合でもログが保持されることを示すことです。OptionWriterNone

最後に

これらの例List[String]では、ログタイプとして使用されています。ただし、使用するStringことが最善の方法とは言えず、ロギングフレームワークによって強制されるいくつかの規則があります。たとえば、カスタムログADTを定義し、出力する必要がある場合は、できるだけ遅く文字列に変換することをお勧めします。このようにして、ログのADTをシリアル化し、後でプログラムで簡単に分析できます(文字列を解析する代わりに)。

WriterTロギングを容易にするために使用できる便利なメソッドが多数あり、ソースを確認してください。たとえば、を指定するw: WriterT[...]と、を使用して新しいログエントリを追加したり、などw :++> List("other event")を使用して現在保持されている値を使用してログを記録したりできます。w :++>> ((v) => List("the result is " + v))

例には、多くの明示的で長いコード(タイプ、呼び出し)があります。いつものように、これらは明確にするためのものであり、一般的な型と操作を抽出することにより、コードでそれらをリファクタリングします。

于 2012-08-14T11:59:33.827 に答える
0
type OptionLogger[A] = WriterT[Option, NonEmptyList[String], A]

      val two: OptionLogger[Int] = WriterT.put(2.some)("The number two".pure[NonEmptyList])
      val hundred: OptionLogger[Int] = WriterT.put(100.some)("One hundred".pure[NonEmptyList])

      val twoHundred = for {
        a <- two
        b <- hundred
      } yield a * b

      twoHundred.value must be equalTo(200.some)


      val log = twoHundred.written map { _.list } getOrElse List() mkString(" ")
      log must be equalTo("The number two One hundred")
于 2012-08-15T00:21:58.790 に答える