我想做以下事情:方法getSessionToken
:
如果“cachedSessionToken
”已过时(超过1小时前发布),请向服务器发出请求
一旦向服务器发出请求,它会将结果保存为“variable
”,然后在调用getSessionToken
时将其返回
因此,想法是缓存结果并在需要时返回,否则首先向服务器getSessionTokenRemote
发出请求,缓存它并且仅在返回结果之后。我对getSessionTokenRemote
的确切实现不感兴趣,但是,它可能是这样的:
getSessionTokenRemote :: IO String
-- or just
getSessionTokenRemote :: String
无论最合适的是什么。
我想知道,我怎么用纯函数语言如Haskell来做到这一点?
答案 0 :(得分:5)
如果您希望getSessionTokenRemote
写入变量,则必须将其传递给一个变量,例如给它一个类似的类型:
type CacheInfo = {- you've got to fill this bit in -}
getSessionTokenRemote :: IORef CacheInfo -> IO String
一旦你选择了这样的类型,实现就不会太难了。
另一种方法是,如果你进入实现隐藏,就是写一个产生getSessionTokenRemote
值的值。假设上面的类型的内部实现和一些空的缓存值emptyCacheInfo
,可以这样做:
getGetSessionTokenRemote :: IO (IO String)
getGetSessionTokenRemote = getSessionTokenRemote <$> newIORef emptyCacheInfo
这是一个IO
操作,在执行时会产生一个带有新缓存的会话令牌获取操作。
答案 1 :(得分:3)
Haskell确实有状态变量,但它们是“被管理的”。它们不可避免地在某些monad中运行,你必须明确地读/写它们。 IORef
,MVar
,TVar
和朋友都是'管理参考'(我第一次听到Clojure社区中使用的术语)。
这是一个可怕的简化示例,说明如何使用ref类型来设置内容。
import Data.IORef
import Network.HTTP
data SessionToken = SessionToken {
_timeHours :: Int
, _token :: String
}
getSessionToken :: IORef SessionToken -> IO String
getSessionToken cacheRef = do
cachedToken <- readIORef cacheRef
if _timeHours cachedToken > 1
then do
newToken <- getSessionTokenRemote
writeIORef cacheRef newToken
return $ _token newToken
else return $ _token cachedToken
getSessionTokenRemote :: IO SessionToken
getSessionTokenRemote = do
tokenRequest <- simpleHTTP $ getRequest "http://jtobin.ca/sample_token.txt"
token <- getResponseBody tokenRequest
return $ SessionToken 0 token
main :: IO ()
main = do
tokenRef <- newIORef $ SessionToken 0 "my token"
getSessionToken tokenRef >>= putStrLn
writeIORef tokenRef $ SessionToken 2 "my token"
getSessionToken tokenRef >>= putStrLn