Log in function or function using it?

后端 未结 2 750
眼角桃花
眼角桃花 2021-01-19 05:10

Is it best (I\'m aware of that there\'s no silver bullet, but there may be some advantage by using one over the other) - to log in the calling function, or the func

2条回答
  •  粉色の甜心
    2021-01-19 05:36

    Approach 2 is better. In general, logging is a Cross-Cutting Concern, so it's best decoupled from implementation details. Cross-Cutting Concerns are best addressed via Composition; in OOD, this can be done with Decorators or Interceptors. In FP, we can sometimes learn from OOD, because many of the principles translate from objects to closures.

    However, instead of using Approach 2 above verbatim, I'd rather prefer something like this:

    module MongoDb =
        let tryGetServer connectionString =
            try
                let server = MongoClient(connectionString).GetServer()
                server.Ping()
                Some server
            with _ -> None
    

    Notice that the MongoDb module has no knowledge of logging. This follows the Single Responsibility Principle, which is also valuable in Functional Programming.

    The tryGetServer function has this signature:

    string -> MongoServer option
    

    Now you can define a logging function, totally decoupled from the MongoDb module:

    module XyzLog =
        type Logger() =
            member this.Information message = ()
    
        let tryGetServer f (logger : Logger) connectionString  =
            match f connectionString with
            | None ->
                logger.Information "Unable to connect to the database server."
                None
            | Some srv ->
                logger.Information "Successfully connected to the database server."
                Some srv
    

    Here, you can imagine that XyzLog is a placeholder for a particular logging module, utilising Serilog, Log4Net, NLog, your own custom logging framework, or similar...

    The f argument is a function with the generic signature 'a -> 'b option, of which MongoDb.tryGetServer is a specialization.

    This means that you can now define a partially applied function like this:

    let tgs = XyzLog.tryGetServer MongoDb.tryGetServer (XyzLog.Logger())
    

    The function tgs also has the signature

    string -> MongoServer option
    

    So any client that depends on a function with this signature can use MongoDb.tryGetServer or tgs interchangeably, without knowing the difference.

    This enables you to change you mind or refactor both MongoDb.tryGetServer and your logging infrastructure independently of each other.

提交回复
热议问题