简体   繁体   English

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

[英]Aeson parse JSON and use State to keep it in memory

I have a "settings" file in my Haskell learning project. 我的Haskell学习项目中有一个“设置”文件。 It's a JSON file with a basic structure for defining some settings. 这是JSON文件,具有用于定义某些设置的基本结构。 The settings are now parsed every time when I try to grab something from it. 现在,每次我尝试从中获取内容时,都会对设置进行解析。 It's reparsed everytime. 每次都会进行修复。 So I figured I have to keep some State for it that it's been parsed and available. 因此,我认为我必须为其保留一些State ,因为该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"

This is the getSetting I am using atm: 这是我正在使用atm的getSetting

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

Now instead of calling parseSettings , I want to actually do something stateful so that I don't have to reparse the settings file everytime I call getSetting . 现在,我不再需要调用parseSettings而是实际上要做一些有状态的操作,这样我每次调用getSetting都不必重新解析设置文件。 Can anyone recommend me blogposts/articles or any pointers at how to use the State monad for this? 谁能推荐我博客文章/文章或任何有关如何使用State monad的指针? (or if there is a simpler way, I'd prefer that). (或者,如果有更简单的方法,我希望这样做)。

Unless you need to modify the configuration during the run of your program, it makes more sense to use Reader . 除非您在程序运行期间需要修改配置,否则使用Reader会更有意义。 You could express your functions using the Reader (or ReaderT , if you need other monads in your stack, like IO etc.) monad and then something like 您可以使用Reader (或ReaderT ,如果您需要堆栈中的其他monad,例如IO等)来表达功能,然后使用诸如

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

Using StateT you can do 使用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)

Then you can use this as 然后,您可以将其用作

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

And you can run it with 你可以用

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

This allows you to worry about parsing the settings the first time you call getSetting , and it'll use the current settings after that. 这使您getSetting担心第一次调用getSetting时就会解析设置,并且此后它将使用当前设置。 If you need to reload the settings just use reloadSettings , and all subsequent commands will use the new settings. 如果您需要重新加载设置,请使用reloadSettings ,所有后续命令将使用新设置。

Note that if you want your settings to always be static throughout the application, it's much better to use the Reader monad as Petr shows. 请注意,如果您希望您的设置在整个应用程序中始终是静态的,那么最好使用Petr显示的Reader monad更好。 This separates the loading of your settings from the running of your application, it has to occur in a two-step process. 这将设置的加载与应用程序的运行分开,它必须分两步进行。 If you do want to allow the settings to change throughout the application then the State monad is necessary. 如果您确实希望在整个应用程序中更改设置,则必须使用State monad。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM