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

时间:2015-04-02 18:33:37

标签: json haskell state

我有一个"设置"我的Haskell学习项目中的文件。它是一个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

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

现在我没有打电话给parseSettings,而是想做一些有状态的事情,这样我每次拨打getSetting时都不必重新设置设置文件。任何人都可以推荐我博客文章/文章或任何关于如何使用State monad的指针? (或者如果有更简单的方法,我更喜欢这样做。)

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:2)

使用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,所有后续命令都将使用新设置。

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