我有一个"设置"我的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的指针? (或者如果有更简单的方法,我更喜欢这样做。)
答案 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。