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 是必需的。
我的 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 是必需的。