我在这些条件下做了什么来遵循FP



我正在读FP,我有两个基本问题:

  1. FP说函数应该取一个输入并给出一个输出。那么,我应该如何处理void方法呢?它什么都不回,对吗?

  2. FP说函数应该有单个resresponsibility,那么我们如何在方法内部处理log语句?这不违反规定吗?

希望知道他们是如何在Scala中处理这些事情的,Haskell。提前谢谢。

我假设你正在读一本名为《函数式编程》的书,尽管了解作者也会有所帮助。无论如何,这些问题相对容易回答,我将给出关于Haskell的答案,因为我不了解Scala。


那么,我应该如何处理void方法呢?它什么都不回,对吗?

在像Haskell这样的纯函数式语言中没有void方法。纯函数没有副作用,所以没有返回值的纯函数是没有意义的,就像一样

f :: Int -> ()
f x = let y = x * x + 3 in ()

不会进行任何计算,y永远不会被计算,并且您提供的所有输入都将返回相同的值。然而,如果你有一个不纯净的函数,比如写文件或在屏幕上打印东西的函数,那么它必须存在于一元上下文中。如果你还不懂单子,别担心。它们需要一些时间来适应,但它们是一个非常强大和有用的抽象,可以让很多问题变得更容易。monad类似于IO,在Haskell中,它采用一个类型参数来指示可以存储在该上下文中的值。所以你可以有这样的东西

putStrLn :: String -> IO ()

-- FYI: FilePath is an alias for String
writeFile :: FilePath -> String -> IO ()

这些都有副作用,用IO something的返回值表示,而()意味着该操作没有任何有意义的结果。例如,在Python3中,print函数返回None,因为在将值打印到屏幕后没有任何有意义的返回。()也可以表示一元上下文具有有意义的值,例如在readFilegetLine:中

getLine :: IO String
readFile :: FilePath -> IO String

在编写main函数时,您可以执行类似的操作

main = do
    putStrLn "Enter a filename:"
    fname <- getLine    -- fname has type String
    writeFile fname "This text will be in a file"
    contents <- readFile fname
    putStrLn "I wrote the following text to the file:"
    putStrLn contents

FP说函数应该有一个resresponsibility,那么我们如何处理方法中的日志语句呢?这不违反规定吗?

大多数函数不需要在其中进行日志记录。我知道这听起来很奇怪,但这是真的。在Haskell和大多数其他函数式语言中,您将编写许多小的、易于测试的函数,每个函数只需执行一步。在您的应用程序中有很多1或2行函数是很常见的。

当你确实需要进行日志记录时,比如说你正在构建一个网络服务器,你可以采取几种不同的方法。实际上,有一个名为Writer的monad,它允许您在执行操作时聚合值。这些操作不一定是不纯净的,也不一定是IO,它们可以是完全纯净的。然而,一个可能用于web服务器或大型应用程序的真正的日志框架可能会附带自己的框架。这样,您就可以设置登录屏幕、文件、网络位置、电子邮件等。这个monad将封装IO monad,以便它可以执行这些副作用。更高级的可能会使用一些更高级的库,如monad transformer或可扩展效果。这些功能可以将不同的monad"组合"在一起,这样就可以同时使用这两种功能的实用程序。你可能会看到类似的代码

type MyApp a = LogT IO a
-- log :: Monad m => LogLevel -> String -> LogT m ()
getConnection :: Socket -> MyApp Connection
getConnection sock = do
    log DEBUG "Waiting for next connection"
    conn <- liftIO $ acceptConnection sock
    log INFO $ "Accepted connection from IP: " ++ show (connectionIP conn)
    return conn

我不希望你能完全理解这段代码,但我希望你能看到它将日志记录和网络操作混合在一起。liftIO函数是monad转换器的常见函数,它将IO操作"转换"为封装IO的新monad。

这听起来可能很令人困惑,如果你习惯了Python、Java或类似C++的语言,一开始可能会感到困惑。我当然是!但当我习惯了用这种不同的方式思考问题后,我希望我一直用OOP语言来思考这些结构。

我可以从Haskell的角度回答。

FP说函数应该取一个输入并给出一个输出。那么,我应该如何处理void方法呢?它什么都不回,对吗?

因为这才是真正的功能!在数学中,每个函数都有一些输入,也有一些输出。在不提供任何输入的情况下,不能期望某些输出。你在其他语言中看到的void方法在数学上没有意义。但实际上,其他语言中的void方法会执行某种IO操作,在Haskell中将其抽象为IOmonad。

我们如何处理方法中的日志语句

您可以使用monad转换器堆栈,并提升IO日志操作以在那里执行。事实上,编写器monad完全可以在没有任何IO活动的情况下进行日志操作。

最新更新