Cop*_*ash 3 haskell functional-programming applicative pure-function
这个问题还有一个额外的限制,那就是IO尽可能避免。
这个限制最初是放在我的问题的最后,似乎很难被注意到。
我实际上知道如何在 Haskell 中实现我的目标,就像我知道如何在其他命令式编程语言中实现它一样 - 命令式方式。
但我没有使用任何其他命令式编程,对吗?我正在使用哈斯克尔。我想要一种 Haskell 方式,一种纯粹的方式。
我通过将额外限制重新定位在相对显眼的位置来重新组织我的问题。那是我的错。非常感谢这些快速回复。
main :: IO ()
main =
putStrLn =<< cachedOrFetched
<$> getLine
<*> getLine
cachedOrFetched :: String -> String -> String
cachedOrFetched cached fetched =
if not $ null cached
then cached
else fetched
Run Code Online (Sandbox Code Playgroud)
上面的代码执行了两次IO。但期望的行为是当第一个 IO 的结果不为 null 时跳过第二个 IO。
我知道我可以通过使用door来实现这一点when。鉴于使用太多dos 违反了我使用 Haskell 的初衷,我可能会接受when.
或者,还有更好的方法?更纯粹的方式?
大约两周前我开始学习 Haskell。我并不期待从中找到工作,只是被编程语言本身所吸引。因为据我所知这是“最纯粹的”。
起初,一切似乎都如我预期的那样好。但后来我发现我必须IO在我的纯代码中写 s 。我花了很长时间才找到一种控制不纯污染的方法。应用函子似乎是拯救者。
有了它,我可以将不纯的 IO“柯里化”到我的纯函数中,这样可以节省大量do,<-和显式IO符号。然而,我遇到了这个问题 - 我无法跳过纯函数中不必要的 IO。
再次,大量搜索和阅读。不幸的是,到目前为止还没有令人满意的答案。
要跳过 IO,您必须告诉 IOcachedOrFetched您正在执行 IO,而不是在读取两行后将两个字符串传递给它。只有这样,它才能有条件地运行第二个getLineIO 操作。当然,cachedOrFetched不一定需要处理IO,它可以适用于任何monad:
main :: IO ()
main = putStrLn =<< cachedOrFetched getLine getLine
cachedOrFetched :: Monad m => m String -> m String -> m String
cachedOrFetched first second = do
cached <- first
if not $ null cached
then return cached
else second
Run Code Online (Sandbox Code Playgroud)
我可能会写
main :: IO ()
main = getLine >>= ifNullDo getLine >>= putStrLn
ifNullDo :: Applicative f => f String -> String -> f String
ifNullDo fetch cached =
if not $ null cached
then pure cached
else fetch
Run Code Online (Sandbox Code Playgroud)
我没有使用任何其他命令式编程,对吧?
是的,你是。如果您正在编写main函数或使用IO类型,那么您正在编写命令式程序,一个接一个的 IO 操作。(纯)程序需要控制何时、是否以及以什么顺序运行putStrLn和getLine命令。
Haskell 只是使命令式编程变得明确,并允许非常容易地从中进行抽象,例如通过交换 monad 来运行模拟而不是实际的 IO。
我想要一种 Haskell 方式,一种纯粹的方式。
Haskell方式是将纯粹的应用逻辑与外界的不纯粹边界分开;非常喜欢端口和适配器架构。当然,这只能到此为止,对于像这样的玩具示例来说,cachedOrFetched很难看出边界应该放在哪里。但在较大的应用程序中,您通常会在业务逻辑中提出自己的“操作”抽象,并仅处理它们而不是IO直接处理它们。