Aeson解析JSON并使用State保存在内存中

Aeson parse JSON and use State to keep it in memory

我的 Haskell 学习项目中有一个 "settings" 文件。这是一个 JSON 文件,具有用于定义某些设置的基本结构。现在,每次我尝试从中获取内容时,这些设置都会被解析。每次都会修复。所以我想我必须保留一些 State 因为它已经被解析并可用。

 parseSettings :: IO Settings
 parseSettings = do
    settingsContent <- B.readFile settingsFile
    let settings = decode settingsContent :: Maybe Settings
    case settings of
        Just s  -> return s
        Nothing -> error "Couldn't parse settings file"

这是getSetting我正在使用的atm:

getSetting :: (Settings -> a) -> IO a
getSetting f = do
    settings <- parseSettings
    return (f settings)

现在我不想调用 parseSettings,而是想做一些有状态的事情,这样我就不必在每次调用 getSetting 时都重新解析设置文件。谁能推荐我 blogposts/articles 或关于如何为此使用 State monad 的任何指示? (或者如果有更简单的方法,我会更喜欢)。

除非你需要在程序运行期间修改配置,否则使用Reader更有意义。您可以使用 Reader(或 ReaderT,如果您的堆栈中需要其他 monad,如 IO 等)monad 来表达您的函数,然后是

main :: IO ()
main = do
  s <- parseSettings
  runReader program s

使用StateT你可以做到

import Control.Monad.State
import Control.Applicative

type App = StateT (Maybe Settings) IO

parseSettings :: IO Settings
parseSettings = undefined  -- Keep your implementation

reloadSettings :: App ()
reloadSettings = do
    settings <- liftIO parseSettings
    put $ Just settings

getSetting :: (Settings -> a) -> App a
getSetting f = get >>= maybe (reloadSettings >> getSetting f) (return . f)

那你就可以用这个了

data Settings = Settings
    { hostname :: String
    , port :: Int
    } deriving (Eq, Show)

sendRequest :: Request -> App Response
sendRequest request = do
    h <- getSetting hostname
    p <- getSetting port
    liftIO $ do
        hndl <- connect h p
        sendRequest hndl request
        readResponse hndl

你可以 运行 它与

runApp :: App a -> IO a
runApp app = evalStateT app Nothing

这让您不必担心第一次调用 getSetting 时解析设置,之后它将使用当前设置。如果您需要重新加载设置,只需使用 reloadSettings,所有后续命令都将使用新设置。

请注意,如果您希望您的设置在整个应用程序中始终保持静态,最好使用 Reader monad,如 Petr 所示。这将您的设置加载与 运行 应用程序的加载分开,它必须分两步进行。如果您确实希望允许在整个应用程序中更改设置,那么 State monad 是必需的。