How do you use scalaz.WriterT for logging?
相关问题
- Unusual use of the new keyword
- Get Runtime Type picked by implicit evidence
- What's the point of nonfinal singleton objects
- I want to trace logs using a Macro multi parameter
- PlayFramework: how to transform each element of a
相关文章
- Gatling拓展插件开发,check(bodyString.saveAs("key"))怎么实现
- how do I log requests and responses for debugging
- RDF libraries for Scala [closed]
- Why is my Dispatching on Actors scaled down in Akk
- How do you run cucumber with Scala 2.11 and sbt 0.
- GRPC: make high-throughput client in Java/Scala
- Android Studio doesn't display logs by package
- Setting up multiple test folders in a SBT project
About monad transformers
This is a very short introduction. You may find more information on haskellwiki or this great slide by @jrwest.
Monads don't compose, meaning that if you have a monad
A[_]
and a monadB[_]
, thenA[B[_]]
can not be derived automatically. However in most cases this can be achieved by having a so-called monad transformer for a given monad.If we have monad transformer
BT
for monadB
, then we can compose a new monadA[B[_]]
for any monadA
. That's right, by usingBT
, we can put theB
insideA
.Monad transformer usage in scalaz
The following assumes scalaz 7, since frankly I didn't use monad transformers with scalaz 6.
A monad transformer
MT
takes two type parameters, the first is the wrapper (outside) monad, the second is the actual data type at the bottom of the monad stack. Note: It may take more type parameters, but those are not related to the transformer-ness, but rather specific for that given monad (like the logged type of aWriter
, or the error type of aValidation
).So if we have a
List[Option[A]]
which we would like to treat as a single composed monad, then we needOptionT[List, A]
. If we haveOption[List[A]]
, we needListT[Option, A]
.How to get there? If we have the non-transformer value, we can usually just wrap it with
MT.apply
to get the value inside the transformer. To get back from the transformed form to normal, we usually call.run
on the transformed value.So
val a: OptionT[List, Int] = OptionT[List, Int](List(some(1))
andval b: List[Option[Int]] = a.run
are the same data, just the representation is different.It was suggested by Tony Morris that is best to go into the transformed version as early as possible and use that as long as possible.
Note: Composing multiple monads using transformers yields a transformer stack with types just the opposite order as the normal data type. So a normal
List[Option[Validation[E, A]]]
would look something liketype ListOptionValidation[+E, +A] = ValidationT[({type l[+a] = OptionT[List, a]})#l, E, A]
Update: As of scalaz 7.0.0-M2,
Validation
is (correctly) not a Monad and soValidationT
doesn't exist. UseEitherT
instead.Using WriterT for logging
Based on your need, you can use the
WriterT
without any particular outer monad (in this case in the background it will use theId
monad which doesn't do anything), or can put the logging inside a monad, or put a monad inside the logging.First case, simple logging
We import the
listMonoid
instance, since it also provides theSemigroup[List]
instance. It is needed sinceWriterT
needs the log type to be a semigroup in order to be able to combine the log values.Second case, logging inside a monad
Here we chose the
Option
monad for simplicity.With this approach, since the logging is inside the
Option
monad, if any of the bound options isNone
, we would just get aNone
result without any logs.Note:
x.point[Option]
is the same in effect asSome(x)
, but may help to generalize the code better. Not lethal just did it that way for now.Third option, logging outside of a monad
Here we use
OptionT
to put theOption
monad inside theWriter
. One of the calculations isNone
to show that even in this case logs are preserved.Final remarks
In these examples
List[String]
was used as the log type. However usingString
is hardly ever the best way, just some convention forced on us by logging frameworks. It would be better to define a custom log ADT for example, and if needed to output, convert it to string as late as possible. This way you could serialize the log's ADT and easily analyse it later programmatically (instead of parsing strings).WriterT
has a host of useful methods to work with to ease logging, check out the source. For example given aw: WriterT[...]
, you may add a new log entry usingw :++> List("other event")
, or even log using the currently held value usingw :++>> ((v) => List("the result is " + v))
, etc.There are many explicit and longish code (types, calls) in the examples. As always, these are for clarity, refactor them in your code by extracting common types and ops.