3

I want to do the following thing: a method getSessionToken which would:

  • make a request to a server if a "cachedSessionToken" is outdated (made more than 1 hour ago)

  • once it has made the request to the server, it saves a result to a "variable" and then returns it whenever getSessionToken is called

So the idea is cache a result and return is when needed, otherwise make the request to the server getSessionTokenRemote first, cache it and only after that return the result. I'm not interested in exact implementation of getSessionTokenRemote, however, it could be something like this:

getSessionTokenRemote :: IO String
-- or just
getSessionTokenRemote :: String

whatever is the best fit.

I wonder, how do I do that in a pure functional language such Haskell?

4

2 回答 2

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动作,当它被执行时,会产生一个带有新缓存的会话令牌获取动作。

于 2013-10-28T03:27:39.163 回答
3

Haskell 确实有状态变量,但它们是“托管的”。它们不可避免地在某些 monad 中运行,您必须显式地读取/写入它们。 IORef, MVar,TVar和friends 都是“托管引用”(我第一次听说这个词是在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
于 2013-10-28T06:37:54.477 回答