3

我有一个爱好网络项目。很简单,只是为了学习 Haskell 和 web 编程。为清楚起见,我使用 Snap 框架。我有以下代码(site.com/auth 处理程序):

auth :: MonadSnap m => m ByteString  
auth = withSession $ \s -> do  
    Just user <- getPostParam "user"
    Just password <- getPostParam "password"
    if user == "demi" && password == "1234"
       then redirect "/"
       else redirect "/login"

withSession读取当前会话并运行参数中的函数。在这里我面临一个问题:用户获得授权,我想为会话添加新值s并使用它运行代码。最好的方法是什么?你会怎么做?假设下面的代码也使用s.

auth另一个问题:我能否以某种方式在处理程序(如)和其他函数中透明地提供上下文?我不想使用以下参数提取所有函数中的所有上下文(如数据库连接、会话和可能其他)ctx

findGoodies :: MonadSnap m => MyContext -> String -> m String
checkCaptcha :: MonadSnap m => MyContext -> m Bool
breakingNews :: MonadSnap m => MyContext -> m ByteString

理想情况下,我想要一个功能withContext,但在处理请求期间可能会更改上下文。我想我可以定义我的 monad 来解决它(对吗?),但是我已经必须使用 Snap monad 并且我不能扩展它(这也是一个问题)?

希望我说得很清楚对我有帮助。

4

1 回答 1

4

您可以将您的MonadSnapmonad 包装在一个StateT以您的上下文作为其状态的状态中。一旦定义了适当的实例,您就可以在新的 monad 中编写可以访问会话状态但仍然可以调用MonadSnap没有lift.

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
import Control.Monad.State

-- StateT wrapper
newtype MySnapT m a = MySnapT { unMySnapT :: StateT MyContext m a }
    deriving ( Monad )

instance MonadTrans MySnapT where
    lift = MySnapT . lift

instance MonadSnap m => MonadSnap (MySnapT m) where
    liftSnap = lift . liftSnap

instance MonadSnap m => MonadState MyContext (MySnapT m) where
    get = MySnapT get
    put = MySnapT . put

runMySnapT :: MonadSnap m => MySnapT m a -> MyContext -> m (a, MyContext)
runMySnapT m = runStateT . unMySnapT $ m

-- wrapper for withSession that runs a MySnapT action with
-- the current session as the StateT state, and sets the
-- resulting state back when it is done
withMySession :: MonadSnap m => MySnapT m a -> m a
withMySession m = do
    (a, s') <- withSession $ runMySnapT m -- read the session and run the action
    setSession s' -- write the session back to disk
    return a        



-- functions that run in the MySnapT monad have access to context as
-- state, but can still call MonadSnap functions
findGoodies :: MonadSnap m => String -> MySnapT m String
findGoodies s = do
    s <- get -- get the session
    put $ modifySession s -- modify and set the session back into the State
    liftSnap undefined -- I can still call Snap functions
    return "Hello"

auth :: MonadSnap m => m String  
auth = withMySession $ do -- use withMySession to run MySnapT actions
    findGoodies "foo"


-- dummy definitions for stuff I don't have

data Snap a = Snap a

class Monad m => MonadSnap m where
  liftSnap :: Snap a -> m a

data MyContext = MyContext

withSession :: MonadSnap m => (MyContext -> m a) -> m a
withSession = undefined

setSession :: MonadSnap m => MyContext -> m ()
setSession = undefined

modifySession :: MyContext -> MyContext
modifySession = undefined
于 2012-05-11T20:51:01.503 回答