3

I'm new in scala programming. I'm puzzled now for how to declare a biz method in a asynchronous and functional way, that the method implementation should include many log message. As a bad practice, I write the code like this :

// trait
trait StoreService {
    def create[Config]: Kleisli[Future, Config, Store]
}

// and the interpreter
trait StoreServiceInterpreter extends StoreService {
    def create[Config]: Kleisli[Future, Config, Store] = Kleisli {cfg => 
        // some implementation ...
        log.info("bla bla bla ...")
        // some implementation ...
        // return a store
        Store(...)
    }
}

It's bad, cause the implementation is with side-effect, log something int the place. So, I change the method declaration like this:

// trait
trait StoreService {
    def create[Config]: Kleisli[Future, Config, Writer[Vector[String], Store]]
}

// and the interpreter
trait StoreServiceInterpreter extends StoreService {
    def create[Config]: Kleisli[Future, Config, Writer[Vector[String], Store]] = Kleisli {cfg => 
        // some implementation ...
        // log.info("bla bla bla ...")
        // some implementation ...
        // return a store
        Writer(Vector("bla bla bla...", Store(...))
    }
}

Using the Writer, the side-effect is eliminated, but the code is not clear:

  • Why a writer returned? Writer[Vector[String], Store] has more noises than Store, has any way to avoid the boilerplate code and remain the no-side-effect?
  • Write log is not ad-hoc! I should build a vector of String to hold the message, using :+ or ++ operation to add log. I think it's not ad-hoc logging, just like write log.info(...) anywhere.
  • For me, I consider logs not material to the correctness of my program. So I choose not to model their side effects in my code. I applaud your embrace of pure FP and look forward to the answer. – Stephen Jun 12 '17 at 07:39
  • Yes, maybe it can be more pure – Zenhue Song Jun 12 '17 at 08:25

1 Answers1

1

Most of Scala developers I know tend to consider logging as "non-side-effect", for convenience. However, if you really want to track them, you might want to take a look on "free monad" concept. Further information: general description, example with logging.

Mine rough explanation is "let's model our program as some AST and interpret it". So, in AST you define a concept of "logging" but not implementation, that comes later, in interpretation. This approach allows you to keep an eye on logging and change the action (from write to /dev/null to async post to external service) without affecting the "business" part of your code.

dveim
  • 3,381
  • 2
  • 21
  • 31
  • Thanks a lot! > "let's model our program as some AST and interpret it". So good! maybe I should add a property for the config named `log`. And the `Free` monad is the best choice if with pure-fp. – Zenhue Song Jun 12 '17 at 08:22