2012-05-11 6 views
3

私は趣味のウェブプロジェクトを持っています。非常に単純な、ちょうどハスケルと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を使用しているとします。

もう1つの質問:ハンドラ(たとえばauthなど)やその他の機能で何らかの理由でコンテキストを透過的に使用できるようにすることはできますか?

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

は、理想的には、私は機能withContextを持っているしたいが、コンテキストが中に変更することがあります。私は、ctxのようなパラメータを持つすべての関数で(DB接続、セッションおよびおそらく他のような)すべてのコンテキストをプルする必要はありません要求を処理する。私は私のモナドを定義して解決するかもしれないと思いますが、スナップモナドを使う必要があります。私はそれを拡張できません(これも問題です)。

希望私は私を助けるためにかなり明確にそれを伝えます。

+0

どのように 'withSession'が現在のセッションを読み込みますか? 'MonadSnap'インスタンス' m'がセッションへのアクセスを提供する場合、 'm'モナド内の値を返すすべての関数も同じ手段でセッションにアクセスする必要があります。 – pat

+0

これは私がhand.Itによって作った自分のセッションです。その名前のサーバー上にクッキーとファイルとして実装されています:)私はWebの内部に触れたいので、セッション管理用のスナップサブシステムをインストールしていません。 – demi

+0

パットのStateTに関する答えは正しいです。それが、私たちがスナップレットとHandlerモナドを作った理由です。ハンドラは実際にはフードの下のStateTです。それはあなたのためのすべての詳細の世話をする。開始するには、[snapletsチュートリアル](http://snapframework.com/docs/tutorials/snaplets-tutorial)を参照してください。 – mightybyte

答えて

4

MonadSnapモナドをStateTにラップすることができます。そのコンテキストは状態として設定されています。適切なインスタンスが定義されたら、新しいモナドにセッション状態にアクセスできる関数を書くことができますが、liftを使わずに関数MonadSnapを呼び出すことはできます。

{-# 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