我更倾向于尽可能地贴近功能范式,当我的大脑迎接挑战时,尽可能接近纯粹的功能。我尽可能使用F#。通常,我会遇到VB.NET或C#(或VBA,当我真的不走运时)。所以我的语言允许我偏离功能方法。
从历史上看,我已经忽略了日志记录并与用户通信,直到我得到结果 - 让用户等待。现在我正在尝试实现状态栏的记录和/或更新。这很容易,因为我的语言允许我随时写入标准输出。但是从一个纯粹的功能角度来看,如何将一个人的功能泄露到外部世界?在计算过程中记录或与用户通信是否与纯功能方法相反?
我确信在Haskell中会使用Monad。使用其他语言时呢?
感谢。
答案 0 :(得分:9)
让我们来看看Haskell的monadic解决方案。日志记录背后的想法是我们的计算有一个额外的方法,可以在某个地方“输出”。有很多方法可以表示这样的计算,但最常见的一种方法是制作monad:
class (Monad m) => MonadWriter w m | m -> w where
tell :: w -> m ()
类型w
表示消息,函数tell
是将消息“发送”到monadic(效果完整)计算中。
注意:
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?