【问题标题】:Logging from within a Functional Programming Paradigm从函数式编程范式中记录
【发布时间】:2012-12-07 21:24:30
【问题描述】:

我更喜欢尽可能贴近功能范式,当我的大脑准备好迎接挑战时,我会尽可能接近纯功能范式。我尽可能使用 F#。通常,我会使用 VB.NET 或 C#(或 VBA,当我真的很不走运时)。所以我的语言让我远离函数式方法。

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

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

谢谢。

【问题讨论】:

  • I prefer to stick as closely as possible to the functional paradigm, squeezing as close as I can get to the purely functional when my brain is up for the challenge 当它给你一些好处时使用它。纯函数式代码通常可能会给系统增加意外的复杂性。
  • 这可能是个好建议...

标签: logging functional-programming purely-functional


【解决方案1】:

让我们看一下 Haskell 的一元解决方案。日志记录背后的想法是,我们的计算有一个额外的方法,可以在某处“输出”写入一条消息。有很多方法可以表示这样的计算,但最通用的一种是创建一个 monad:

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

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

注意事项:

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

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

但上述类型类还有其他可能的、更多面向副作用的(仍然有效的)实现。我们可以定义tell 将消息发送到一些外部接收器,例如到标准输出、文件等。例如:

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

instance MonadWriter String IO where
    tell = putStrLn

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

【讨论】:

  • 类和对象?哇?这不是函数式编程范式,这只是 FP 中的 OOP。
  • 我不明白这一点,Haskell 中的类型类与 OO 类有很大不同。
  • 谢谢。可能我也没有看到重点。在这个答案中,没有类的用例。请关注日志使用的客户端而不是库。谢谢!
  • 看看日志类型如何传递给函数并用于创建和返回日志描述很有趣。我的意思是绝对纯粹的代码部分,没有任何副作用。结果应该在哪里聚合在一起并传递给实际的记录器副作用执行器?在最高级别/一些中间级别,但绝对不在应用程序功能内部。记录结果应该只是记录器的命令。看看一个人如何组合这些结果并将它们与其他功能结合起来很有趣。
【解决方案2】:

我是函数式编程的新手,但这里是 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 对象开始(可能太像一个单例 - 一个 trait 可能比 Scala 更好),或者一个演员来积累日志消息并做任何需要用它们做的事情。

这是比较务实的看法:Logging in Scala

编辑

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

【讨论】:

  • 我喜欢你在这里的思考过程。不过,就我而言,该函数运行时间相当长,我想在它处理时进行报告,而不是等待它返回来报告它的日志。尽管如此,如果没有其他人回答我的问题,我可能会将其标记为答案,因为虽然 Haskell 的答案很好,但我的问题更具体地是关于非 Monad 方法。谢谢!
猜你喜欢
  • 1970-01-01
  • 2010-09-06
  • 1970-01-01
  • 1970-01-01
  • 2014-08-06
  • 2021-10-23
  • 2013-03-28
  • 1970-01-01
相关资源
最近更新 更多