haskell - 如何重构才不会卡在IO monad里?

标签 haskell io monads

我正在创建一种用于交互式日期争论的简单语言。 我想要一个代表“今天”的规则:

import Data.Time
import qualified Text.Parsec as Parsec
import Text.Parsec.String(Parser)

data Date = Date { year  :: Int
                 , month :: Int
                 , day   :: Int
                 }

parseToday :: Parser Date
parseToday = do
  Parsec.string "today"
  now <- getCurrentTime
  let (year, month, day) = toGregorian $ utctDay now
  return $ Date year month day

这不起作用,因为 getCurrentTime返回一个 IO UTCTime 类型的 monad,而解析器的类型为 Parser Date。 当然,获取当前时间需要是一个 IO 操作。 但是,对于我拥有的所有其他解析器,是否没有办法将所有内容都放入 IO monad 中?

最佳答案

一种选择是制作两种类型;一个代表您可能需要解析的所有内容,另一个代表“编译”版本。

data ParsedDate = Today | Specified Date

parseToday :: Parser ParsedDate
parseToday = Today <$ Parsec.string "today"

compile :: ParsedDate -> IO Date
compile Today = do
    (year, month, day) <- toGregorian . utctDay <$> getCurrentTime
    return (Date year month day)
compile (Specified date) = return date

如果您有一个包含数据结构,其中可能包含许多 ParsedDate,您可能需要确保 getCurrentTime 仅被调用一次,以便它们相关即使解析器在时钟即将从一天到下一天滴答作响时正确运行,彼此之间也会保持一致。解决这个问题的第二个选项如下所示:

parseToday :: ParserT ((->) UTCTime) Date
parseToday = do
    Parsec.string "today"
    (year, month, day) <- asks (toGregorian . utctDay)
    return (Date year month day)

这种方法的缺点是您需要预先完成所有的IO;如果 getCurrentTime 是您需要收集的唯一信息,那可能没什么大不了的,但如果您可能有更昂贵的 IO 操作,您只想根据需要执行,第一种方法更好。如果您同时需要一次性执行和按需执行,事情就会变得有点复杂......

关于haskell - 如何重构才不会卡在IO monad里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69999326/

相关文章:

haskell - 如何修复这种嵌套 fmap 仿函数的困惑?

haskell - 在 Haskell 中使用管道符号定义运算符

java - 将字符串写入 .txt 文件不起作用

c# - 写入文件仅在 "step in" Debug模式下有效?

python - 如何解析下面的 .txt 文件?

haskell - 两个应用程序/单子(monad)保持单曲面结构但方式略有不同的实际意义是什么?

haskell - haskell中有 `m a -> (a -> m b) -> m a`函数类型吗?

haskell - Haskell 将 Hom Functor/Monad 称为什么?

haskell - Cabal 要建立一个新的 Haskell 项目吗?

performance - Haskell 性能 : Inversion count algorithm