从功能编程范例中记录

时间:2012-12-07 21:24:31

标签: logging functional-programming purely-functional

我更倾向于尽可能地贴近功能范式,当我的大脑迎接挑战时,尽可能接近纯粹的功能。我尽可能使用F#。通常,我会遇到VB.NET或C#(或VBA,当我真的不走运时)。所以我的语言允许我偏离功能方法。

从历史上看,我已经忽略了日志记录并与用户通信,直到我得到结果 - 让用户等待。现在我正在尝试实现状态栏的记录和/或更新。这很容易,因为我的语言允许我随时写入标准输出。但是从一个纯粹的功能角度来看,如何将一个人的功能泄露到外部世界?在计算过程中记录或与用户通信是否与纯功能方法相反?

我确信在Haskell中会使用Monad。使用其他语言时呢?

感谢。

2 个答案:

答案 0 :(得分:9)

让我们来看看Haskell的monadic解决方案。日志记录背后的想法是我们的​​计算有一个额外的方法,可以在某个地方“输出”。有很多方法可以表示这样的计算,但最常见的一种方法是制作monad:

class (Monad m) => MonadWriter w m | m -> w where
    tell   :: w -> m ()

类型w表示消息,函数tell是将消息“发送”到monadic(效果完整)计算中。

注意:

  • Haskell的MonadWriter实际上更丰富,它包含允许检查和修改w的函数,但现在让我们把它放在一边。
  • | m -> w部分对解释并不重要,只是意味着w对于给定的m是固定的。

最常用的实现是Writer,基本上只是一对。其中一个元素是计算结果,另一个元素是一系列书面消息。 (实际上它不是一个真正的序列,它更通用 - 一个monoid,它定义了将多个消息组合成一个的操作。)你可以通过查看Writer module来检查Haskell的解决方案。然而,更常见的是,使用WriterT monad变换器,所以如果你不是monad粉丝,那么它很难阅读。同样的事情也可以在其他函数语言中完成,例如参见this example in Scala

但是上述类型类还有其他可能的,更多的侧面效果(仍然是功能性的)实现。我们可以定义tell向一些外部接收器发送消息,比如stdout,发送到文件等。例如:

{-# LANGUAGE FunctionalDependencies, TypeSynonymInstances, FlexibleInstances #-}

instance MonadWriter String IO where
    tell = putStrLn

这里我们说IO可以用作将String写入stdout的日志记录工具。 (这只是一个简化的示例,完整的实现可能会有一个monad转换器,可以为任何基于tell的monad添加IO功能。)

答案 1 :(得分:4)

我是函数式编程的新手,但这是Scala的尝试:

object FunctionalLogging {

  type Result = Int

  class ResultWithLogging(val log: List[String], val result: Result) {}

  def functionWithLogging(log: List[String], arg: String): ResultWithLogging = {
    def function(arg: String): Result = arg.length

    new ResultWithLogging(log :+ ("Calling function(" + arg +")"), function(arg))
  }

  val result = functionWithLogging(List(), "Hello world!")

  // -- Pure functional code ends here --
  println("Result = " + result.result)
  println("Log = " + result.log)
}

它的功能在于没有副作用,但显然日志是函数参数和返回的一部分,所以它不是很优雅或实用。

在我看来,根据定义,日志记录是一个理想的副作用,所以如果你同意我的定义,问题是如何将非功能性与功能性代码隔离开来。在实践中,我可能会从一个Scala对象开始(可能太像一个单例 - 一个特性可能更好Scala),或者一个演员来累积日志消息并做任何需要用它们完成的事情。

这是一个更实用的观点:Logging in Scala

修改

这个问题谈到了Haskell Monads和IO:What other ways can state be handled in a pure functional language besides with Monads?