haskell - 如何在纯函数中跳过不必要的 IO?

标签 haskell functional-programming applicative pure-function

更新

这个问题有一个额外的限制, 这是为了避免IO尽可能的。

这个限制最初是放在我问题的最后, 这似乎很难被注意到。

我实际上知道如何在 Haskell 中实现我的目标, 就像我知道如何在其他命令式编程语言中做到这一点一样——命令式的方式。

但我没有使用任何其他命令式编程,对吗? 我正在使用 Haskell。 我想要一种 Haskell 方式,一种纯粹的方式。

我通过将额外限制重新定位在相对显眼的位置来重新组织我的问题。 那是我的错。 非常感谢这些快速响应。

原始问题

main :: IO ()
main =
    putStrLn =<< cachedOrFetched
        <$> getLine
        <*> getLine

cachedOrFetched :: String -> String -> String
cachedOrFetched cached fetched =
    if not $ null cached
    then cached
    else fetched

上面的代码执行了两次IO。 但期望的行为是跳过第二个 IO 当第一个 IO 的结果不为空时。

我知道我可以通过使用 do 来实现这一点或 when . 由于使用了太多 do s违背了我使用Haskell的初衷, 我可能会和 when 一起生活.

或者有更好的方法吗?更纯粹的方式?

这就是全部内容

大约两周前我开始学习 Haskell。 我不期待它的工作, 但只是被编程语言本身所吸引。 因为据我所知,它是“最纯粹的”。

起初,一切似乎都和我预期的一样好。 但后来我发现我必须写 IO s 在我的纯代码中。 我花了很长时间才找到一种方法来控制不纯污染。 Applicative Functor 似乎是救星。

有了它,我可以将不纯的 IO “curry” 到我的纯函数中, 节省了很多 do , <-和明确的IO符号。 但是,我遇到了这个问题 - 我无法跳过纯函数中不必要的 IO。

再次,大量搜索和阅读。 不幸的是,到目前为止还没有令人满意的答案。

引用文献

最佳答案

要跳过 IO,您必须告诉 cachedOrFetched 您正在执行 IO,而不是在 读取两行之后将两个字符串传递给它。只有这样,它才能有条件地运行第二个 getLine IO 操作。当然,cachedOrFetched 不一定需要处理IO,它可以为any 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

我可能会写

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

I am not using any other imperative programming, right?

是的,你是。如果您正在编写 main 函数或使用 IO 类型,那么您正在编写一个命令式程序,一个 IO Action 接一个接一个。 (纯)程序需要控制何时、是否以及以什么顺序运行 putStrLngetLine 命令。

Haskell 只是明确了命令式编程,并允许非常容易地从中抽象出来,例如通过换出 monad 来运行模拟而不是实际的 IO。

I'd like a Haskell way, a pure way.

Haskell 方式是将纯应用逻辑与不纯边界分离到外界;非常喜欢ports and adapters architecture .当然,这只能到此为止,对于 cachedOrFetched 之类的玩具示例,很难看出在哪里放置边界。但在较大的应用程序中,您通常会为业务逻辑中的“ Action ”提出自己的抽象,并且只处理它们而不是直接处理 IO

关于haskell - 如何在纯函数中跳过不必要的 IO?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70226458/

相关文章:

functional-programming - 奇怪的 Haskell/GHCi 问题

F# 多条件 if/else 与匹配

Haskell 守护进程无法从 Redis 中提取 `getAddrInfo`/`Too many open files` 错误

c# - 函数式语言编译器相对于命令式语言编译器的优势

c# - 函数式编程中的对象 - 不变性

haskell - Traversables 的自然法则是什么意思?

parsing - 使用替代方案的纯应用解析器

haskell - 为 Reader r 编写 Applicative 实例时如何编写 <*>

haskell:99 个问题 #7:异构列表

haskell - 有没有更好的方法使用有理数来实现斐波那契公式?