In this series of blogs, we've covered functional versions of comparers, table parsers, and just "code."
It's time to revisit an earlier functional topic: loggers (see Spy). Spy was OK but the syntax was not ideal. In particular, it required an extra set of parentheses which had to be removed when removing the log messages. So, over the last few years, I have created a new functional logger called Flog. You can find it at Flog. Take a look, in particular, at the README.md file, and also under the test directory: FlogExamples.sc as well as other spec files in the test directory.
I have also written about it on Medium.
So, what is the real difference between Spy and Flog? It's mostly down to the use of an implicit class. What's that?
Implicit classes, like the other implicit things (vals, defs, objects) are an especially powerful bit of magic. If some code requires a particular method, say, propertyA (defined for class Y) and you try to invoke it on an instance of class X which does not have such a method, the compiler is going to look for an implicit converter from X to Y. This could be declared as an implicit def name(x: X): Y, or an implicit val of type X=>Y, or as the constructor of an implicit class Y which takes exactly one parameter, an X. This is a similar mechanism that allows for additional methods on String that belong to the StringOps class, such as r for regex.
In the case of Flog, the name (not that it matters) of the implicit class is Flogger. It defines a number of methods such as def !![X: Loggable](x: => X): X which performs info-level logging for X and various compound types of X such as Iterable[X], Option[X], etc. There are similar methods for debug (!?) and trace (!??).
The real utility of the library is in the various implicits that allow for the rendering of different types and types of types. Flog can handle Iterable[X], Option[X], Map[K, V], Future[X], Try[X], as well as any case class (or other Product).