on-a-horse 0.1 → 0.2
raw patch · 8 files changed
+264/−81 lines, 8 filesdep +timePVP ok
version bump matches the API change (PVP)
Dependencies added: time
API changes (from Hackage documentation)
- Control.Arrow.Transformer.Automaton.Monad: instance (Arrow a) => ArrowAddAutomaton (Automaton a) a
- Control.Arrow.Transformer.Automaton.Monad: instance (Arrow a, Arrow a', ArrowAddAutomaton a a') => ArrowAddAutomaton (ReaderArrow r a) (ReaderArrow r a')
- Control.Arrow.Transformer.Automaton.Monad: instance (Arrow a, Arrow a', ArrowAddAutomaton a a') => ArrowAddAutomaton (StateArrow s a) (StateArrow s a')
- Control.Arrow.Transformer.Automaton.Monad: instance (ArrowChoice a, ArrowChoice a', ArrowAddAutomaton a a') => ArrowAddAutomaton (ErrorArrow ex a) (ErrorArrow ex a')
- Control.Arrow.Transformer.LabeledArrow: instance (Arrow a, Arrow a', ArrowAddAutomaton a a') => ArrowAddAutomaton (LabeledArrow a) (LabeledArrow a')
- Web.Horse: auto :: Automaton a i o -> a i (o, Automaton a i o)
- Web.Horse: keepState :: (ArrowApply a', ArrowAddAutomaton a a') => o -> a (Maybe o) o
- Web.Horse: once :: (ArrowAddAutomaton a a', ArrowApply a') => a1 -> a i (Maybe a1)
- Web.Horse: replaceSecond :: (ArrowAddAutomaton a a', ArrowApply a') => a i o -> a (i, Maybe (a i o)) o
- Web.Horse: type WithError ex i o = LabeledArrow (ErrorArrow ex (ReaderArrow FormIn (Automaton (Kleisli IO)))) i o
- Web.Horse: valForm :: (ArrowReader FormIn a', ArrowAddLabel a a', ArrowApply a'1, ArrowAddAutomaton a' a'1, ArrowChoice a') => String -> a' String (Either String a1) -> String -> a () (Html (), Maybe a1)
+ Control.Arrow.Transformer.Automaton.Maybe: MaybeAutomaton :: (a i (o, Maybe (Automaton a i o))) -> MaybeAutomaton a i o
+ Control.Arrow.Transformer.Automaton.Maybe: instance (Arrow a) => Arrow (MaybeAutomaton a)
+ Control.Arrow.Transformer.Automaton.Maybe: instance (Arrow a) => Category (MaybeAutomaton a)
+ Control.Arrow.Transformer.Automaton.Maybe: instance (ArrowChoice a) => ArrowChoice (MaybeAutomaton a)
+ Control.Arrow.Transformer.Automaton.Maybe: newtype MaybeAutomaton a i o
+ Control.Arrow.Transformer.Automaton.Monad: elimMaybeAutomaton :: (ArrowAddAutomaton a may a') => may i o -> a' i (o, Maybe (a i o))
+ Control.Arrow.Transformer.Automaton.Monad: fromMaybeAutomaton :: (ArrowAddAutomaton a may a') => may i o -> a i o
+ Control.Arrow.Transformer.Automaton.Monad: instance (Arrow a, Arrow a', ArrowAddAutomaton a may a') => ArrowAddAutomaton (ReaderArrow r a) (ReaderArrow r may) (ReaderArrow r a')
+ Control.Arrow.Transformer.Automaton.Monad: instance (Arrow a, Arrow may, Arrow a', ArrowAddAutomaton a may a') => ArrowAddAutomaton (StateArrow s a) (StateArrow s may) (StateArrow s a')
+ Control.Arrow.Transformer.Automaton.Monad: instance (ArrowChoice a, ArrowApply a) => ArrowAddAutomaton (Automaton a) (MaybeAutomaton a) a
+ Control.Arrow.Transformer.Automaton.Monad: instance (ArrowChoice a, ArrowChoice may, ArrowChoice a', ArrowAddAutomaton a may a') => ArrowAddAutomaton (ErrorArrow ex a) (ErrorArrow ex may) (ErrorArrow ex a')
+ Control.Arrow.Transformer.Automaton.Monad: liftMaybeAutomaton :: (ArrowAddAutomaton a may a') => a' i (o, Maybe (a i o)) -> may i o
+ Control.Arrow.Transformer.Automaton.Monad: toMaybeAutomaton :: (ArrowAddAutomaton a may a') => a i o -> may i o
+ Control.Arrow.Transformer.LabeledArrow: instance (Arrow a, Arrow a', ArrowAddAutomaton a may a') => ArrowAddAutomaton (LabeledArrow a) (LabeledArrow may) (LabeledArrow a')
+ Web.Horse: catchMayAuto :: (ArrowAddAutomaton a may a') => LabeledArrow (ErrorArrow (LabeledArrow a t1 o) may) t1 o -> LabeledArrow may t1 o
+ Web.Horse: enumForm :: (ArrowAddAutomaton a1 may a', ArrowAddLabel a may, ArrowReader FormIn may) => String -> [(String, b)] -> a () (Html (), Maybe b)
+ Web.Horse: type HoHErr ex i o = LabeledArrow (ErrorArrow ex (ReaderArrow FormIn (Automaton (Kleisli IO)))) i o
+ Web.Horse: type HoHErrMay ex i o = LabeledArrow (ErrorArrow ex (ReaderArrow FormIn (MaybeAutomaton (Kleisli IO)))) i o
+ Web.Horse: type HoHMay i o = LabeledArrow (ReaderArrow FormIn (MaybeAutomaton (Kleisli IO))) i o
+ Web.Horse: withInput0 :: (ArrowReader FormIn a', ArrowAddLabel a a') => a' (e, String, Maybe String) b -> a e b
- Control.Arrow.Transformer.Automaton.Monad: autoToMonad :: (ArrowApply a', ArrowAddAutomaton a a') => a i (Either o z) -> i -> ContT (o, a i o) (ArrowMonad a') z
+ Control.Arrow.Transformer.Automaton.Monad: autoToMonad :: (ArrowApply a', ArrowAddAutomaton a may a') => a i (Either o z) -> i -> ContT (o, a i o) (ArrowMonad a') z
- Control.Arrow.Transformer.Automaton.Monad: class ArrowAddAutomaton a a' | a -> a'
+ Control.Arrow.Transformer.Automaton.Monad: class (ArrowChoice a, ArrowChoice may, ArrowChoice a', ArrowApply a') => ArrowAddAutomaton a may a' | a -> a', a -> may, may -> a
- Control.Arrow.Transformer.Automaton.Monad: co :: (ArrowApply a', ArrowAddAutomaton a a') => o -> ContT (o, a i o) (ArrowMonad a') i
+ Control.Arrow.Transformer.Automaton.Monad: co :: (ArrowApply a', ArrowAddAutomaton a may a') => o -> ContT (o, a i o) (ArrowMonad a') i
- Control.Arrow.Transformer.Automaton.Monad: constantAutomaton :: (ArrowAddAutomaton a a') => a' i o -> a i o
+ Control.Arrow.Transformer.Automaton.Monad: constantAutomaton :: (ArrowAddAutomaton a may a') => a' i o -> a i o
- Control.Arrow.Transformer.Automaton.Monad: dispatch :: (Ord k, ArrowAddAutomaton a a', ArrowApply a') => (k -> a i o) -> a (i, k) o
+ Control.Arrow.Transformer.Automaton.Monad: dispatch :: (ArrowAddAutomaton a may a', Ord k) => (k -> may i o) -> may (i, k) o
- Control.Arrow.Transformer.Automaton.Monad: elimAutomaton :: (ArrowAddAutomaton a a') => a i o -> a' i (o, a i o)
+ Control.Arrow.Transformer.Automaton.Monad: elimAutomaton :: (ArrowAddAutomaton a may a') => a i o -> a' i (o, a i o)
- Control.Arrow.Transformer.Automaton.Monad: liftAutomaton :: (ArrowAddAutomaton a a') => a' i (o, a i o) -> a i o
+ Control.Arrow.Transformer.Automaton.Monad: liftAutomaton :: (ArrowAddAutomaton a may a') => a' i (o, a i o) -> a i o
- Control.Arrow.Transformer.Automaton.Monad: monadToAuto :: (ArrowAddAutomaton a a', ArrowApply a') => (i -> ContT (o, a i o) (ArrowMonad a') z) -> a i o
+ Control.Arrow.Transformer.Automaton.Monad: monadToAuto :: (ArrowAddAutomaton a may a', ArrowApply a') => (i -> ContT (o, a i o) (ArrowMonad a') z) -> a i o
- Web.Horse: catchAuto :: (ArrowAddAutomaton a a', ArrowApply a', ArrowChoice a', ArrowChoice a) => (LabeledArrow (ErrorArrow (LabeledArrow a i o) a)) i o -> LabeledArrow a i o
+ Web.Horse: catchAuto :: (ArrowAddAutomaton a may a') => (LabeledArrow (ErrorArrow (LabeledArrow a i o) a)) i o -> LabeledArrow a i o
- Web.Horse: filterDiffs :: (Eq i, ArrowApply a', ArrowAddAutomaton a a') => a i (Maybe i)
+ Web.Horse: filterDiffs :: (ArrowAddAutomaton a may a', Eq i, ArrowApply a') => a i (Maybe i)
- Web.Horse: getSessionMVar :: MVar (Map String (MVar a)) -> a -> Env -> IO (MVar a, [(String, String)])
+ Web.Horse: getSessionMVar :: MVar [(String, MVar (Automaton (Kleisli IO) Env Response))] -> Env -> IO (Maybe (MVar (Automaton (Kleisli IO) Env Response)))
- Web.Horse: runHorse1 :: ((Env -> IO Response) -> IO ()) -> Automaton (Kleisli IO) Env Response -> IO ()
+ Web.Horse: runHorse1 :: ((Env -> IO Response) -> IO ()) -> MaybeAutomaton (Kleisli IO) Env Response -> IO ()
- Web.Horse: runWeb :: MVar (Map String (MVar (Automaton (Kleisli IO) Env Response))) -> Automaton (Kleisli IO) Env Response -> Env -> IO Response
+ Web.Horse: runWeb :: MVar [(String, MVar (Automaton (Kleisli IO) Env Response))] -> MaybeAutomaton (Kleisli IO) Env Response -> Env -> IO Response
- Web.Horse: withInput :: (ArrowReader FormIn a', ArrowAddLabel a a') => a' (e, String, Maybe String) b -> a e b
+ Web.Horse: withInput :: (ArrowReader FormIn a', ArrowAddLabel a a', ArrowAddAutomaton a1 a' a'1) => a1 (e, String, Maybe String) b -> a e b
Files
- Control/Arrow/Transformer/Automaton/Maybe.hs +69/−0
- Control/Arrow/Transformer/Automaton/Monad.hs +60/−19
- Control/Arrow/Transformer/LabeledArrow.hs +11/−2
- Web/Horse/Forms.hs +27/−15
- Web/Horse/Forms/Types.hs +38/−9
- Web/Horse/Hack.hs +46/−24
- on-a-horse.cabal +6/−5
- tutorial.lhs +7/−7
+ Control/Arrow/Transformer/Automaton/Maybe.hs view
@@ -0,0 +1,69 @@+module Control.Arrow.Transformer.Automaton.Maybe where++import Control.Arrow+import qualified Control.Category as C+import Control.Arrow.Operations+import qualified Control.Arrow.Transformer as AT+import Control.Arrow.Transformer.All++import Data.Maybe+import qualified Data.Map as M++--A MaybeAutomaton returns either Just f to indicate a new Automaton,+--or Nothing to indicate 'no change'/'do the same thing'.+newtype MaybeAutomaton a i o = + MaybeAutomaton (a i (o, Maybe (Automaton a i o)))++mAut (MaybeAutomaton f) = f++automatonFromMaybe f = + Automaton (mAut f >>> second (arr (fromMaybe (automatonFromMaybe f))))++maybeFromAutomaton (Automaton f) = + MaybeAutomaton (f >>> second (arr Just))+++--Why use MaybeAutomata? The arrow instances coalesce Nothings,+--reducing memory use.++instance (Arrow a) => C.Category (MaybeAutomaton a) where+ (.) f g = + MaybeAutomaton $ (mAut g) >>> (mAut f *** arr id) >>> + arr (\((o,f'),g') -> (o, + case (f',g') of+ (Nothing,Nothing) -> Nothing+ (_,_) -> Just $ + fromMaybe (automatonFromMaybe f) f'+ C..+ fromMaybe (automatonFromMaybe g) g'))++ id = MaybeAutomaton (C.id >>> arr (flip (,) Nothing))++instance (Arrow a) => Arrow (MaybeAutomaton a) where+ arr f = MaybeAutomaton (arr f >>> arr (flip (,) Nothing))+ ++ (***) f g = MaybeAutomaton $ (mAut f *** mAut g) >>> + arr (\((o1,f'),(o2,g')) -> + ((o1,o2),+ case (f',g') of+ (Nothing,Nothing) -> Nothing+ (_,_) -> Just $ + fromMaybe (automatonFromMaybe f) f'+ ***+ fromMaybe (automatonFromMaybe g) g'))++ first f = f *** arr id+ second f = arr id *** f+ ++instance (ArrowChoice a) => ArrowChoice (MaybeAutomaton a) where+ (+++) f g = MaybeAutomaton $ mAut f +++ mAut g >>>+ arr (\x -> case x of+ Left (o,f') -> (Left o, + fmap (+++ automatonFromMaybe g) f')+ Right (o,g') -> (Right o, + fmap (automatonFromMaybe f +++) g'))++ left f = f +++ arr id+ right f = arr id +++ f
Control/Arrow/Transformer/Automaton/Monad.hs view
@@ -2,7 +2,7 @@ UndecidableInstances, FunctionalDependencies, NoMonomorphismRestriction #-} module Control.Arrow.Transformer.Automaton.Monad- (monadToAuto, co, autoToMonad, readerArrow, swapsnd,+ (readerArrow, swapsnd, autoToMonad, co, monadToAuto, pushError,popError,rstrength, ArrowAddAutomaton (..), dispatch) where @@ -14,6 +14,7 @@ import Control.Arrow.Operations import qualified Control.Arrow.Transformer as AT import Control.Arrow.Transformer.All+import Control.Arrow.Transformer.Automaton.Maybe import Data.Maybe import qualified Data.Map as M@@ -21,20 +22,20 @@ unAM (ArrowMonad f) = f monadToAuto- :: (ArrowAddAutomaton a a', ArrowApply a') =>+ :: (ArrowAddAutomaton a may a', ArrowApply a') => (i -> ContT (o, a i o) (ArrowMonad a') z) -> a i o monadToAuto f = liftAutomaton (proc i -> unAM ((f i) `runContT` (error "automaton ended")) -<< ()) co- :: (ArrowApply a', ArrowAddAutomaton a a') =>+ :: (ArrowApply a', ArrowAddAutomaton a may a') => o -> ContT (o, a i o) (ArrowMonad a') i co o = ContT (\fi -> return (o, liftAutomaton (proc i -> unAM (fi i) -<< ()))) autoToMonad- :: (ArrowApply a', ArrowAddAutomaton a a') =>+ :: (ArrowApply a', ArrowAddAutomaton a may a') => a i (Either o z) -> i -> ContT (o, a i o) (ArrowMonad a') z@@ -46,64 +47,104 @@ -class ArrowAddAutomaton a a' | a -> a' where+class (ArrowChoice a, ArrowChoice may, ArrowChoice a', ArrowApply a') + => ArrowAddAutomaton a may a' | a -> a', a -> may, may -> a where elimAutomaton :: a i o -> a' i (o, a i o) liftAutomaton :: a' i (o, a i o) -> a i o constantAutomaton :: a' i o -> a i o -instance (Arrow a) => ArrowAddAutomaton (Automaton a) a where+ toMaybeAutomaton :: a i o -> may i o+ fromMaybeAutomaton :: may i o -> a i o+ liftMaybeAutomaton :: a' i (o, Maybe (a i o)) -> may i o+ elimMaybeAutomaton :: may i o -> a' i (o, Maybe (a i o))++instance (ArrowChoice a, ArrowApply a) => + ArrowAddAutomaton (Automaton a) (MaybeAutomaton a) a where+ elimAutomaton (Automaton f) = f liftAutomaton = Automaton constantAutomaton f = Automaton (f >>> arr (flip (,) (constantAutomaton f))) -instance (Arrow a, Arrow a', ArrowAddAutomaton a a') - => ArrowAddAutomaton (StateArrow s a) (StateArrow s a') where+ toMaybeAutomaton = maybeFromAutomaton+ fromMaybeAutomaton = automatonFromMaybe+ liftMaybeAutomaton = MaybeAutomaton+ elimMaybeAutomaton = mAut++instance (Arrow a, Arrow may, Arrow a', ArrowAddAutomaton a may a') + => ArrowAddAutomaton (StateArrow s a) (StateArrow s may) (StateArrow s a') where elimAutomaton = autoState . elimAutomaton . runState liftAutomaton = stateArrow . liftAutomaton . stateAuto constantAutomaton = stateArrow . constantAutomaton . runState++ toMaybeAutomaton = stateArrow . toMaybeAutomaton . runState+ fromMaybeAutomaton = stateArrow . fromMaybeAutomaton . runState+ liftMaybeAutomaton = error "not implemented yet"+ elimMaybeAutomaton = error "not implemented yet" instance (ArrowState s a, ArrowApply a) => (MonadState s (ArrowMonad a)) where put s = ArrowMonad (proc () -> store -< s) get = ArrowMonad fetch -instance (Arrow a, Arrow a', ArrowAddAutomaton a a') - => ArrowAddAutomaton (ReaderArrow r a) (ReaderArrow r a') where+instance (Arrow a, Arrow a', ArrowAddAutomaton a may a') + => ArrowAddAutomaton (ReaderArrow r a) (ReaderArrow r may) (ReaderArrow r a') where elimAutomaton = (>>> (second (arr readerArrow))) . readerArrow . elimAutomaton . runReader + elimMaybeAutomaton = (>>> (second (arr (fmap readerArrow)))) .+ readerArrow . elimMaybeAutomaton . runReader+ liftAutomaton = readerArrow . liftAutomaton . (>>> (second (arr runReader))) . runReader++ liftMaybeAutomaton = readerArrow . liftMaybeAutomaton .+ (>>> (second (arr (fmap runReader)))) . runReader + constantAutomaton = readerArrow . constantAutomaton . runReader+ toMaybeAutomaton = readerArrow . toMaybeAutomaton . runReader+ fromMaybeAutomaton = readerArrow . fromMaybeAutomaton . runReader -instance (ArrowChoice a, ArrowChoice a', ArrowAddAutomaton a a')- => ArrowAddAutomaton (ErrorArrow ex a) (ErrorArrow ex a') where+instance (ArrowChoice a, ArrowChoice may, ArrowChoice a', ArrowAddAutomaton a may a')+ => ArrowAddAutomaton (ErrorArrow ex a) (ErrorArrow ex may) (ErrorArrow ex a') where elimAutomaton = pushError . (>>> second (arr pushError) >>> arr rstrength) . elimAutomaton . popError + elimMaybeAutomaton = pushError .+ (>>> second (arr (fmap pushError)) >>> arr rstrength)+ . elimMaybeAutomaton . popError+ liftAutomaton f = pushError $ liftAutomaton $ (>>> arr (revrstrength (liftAutomaton f)) >>> second (arr popError)) $ popError f + liftMaybeAutomaton f =+ pushError $ liftMaybeAutomaton $+ (>>> arr (revrstrength (Just $ fromMaybeAutomaton $ liftMaybeAutomaton f))+ >>> second (arr (fmap popError)))+ $ popError f+ constantAutomaton = pushError . constantAutomaton . popError+ toMaybeAutomaton = pushError . toMaybeAutomaton . popError+ fromMaybeAutomaton = pushError . fromMaybeAutomaton . popError dispatch = dispatch0 M.empty dispatch0- :: (Ord k,- ArrowAddAutomaton a a',- ArrowApply a') =>- M.Map k (a i o) -> (k -> a i o) -> a (i, k) o-dispatch0 mp def = liftAutomaton $ proc (i,k) -> do+ :: (ArrowAddAutomaton a may a',+ Ord k) =>+ M.Map k (may i o) -> (k -> may i o) -> may (i, k) o+dispatch0 mp def = liftMaybeAutomaton $ proc (i,k) -> do let f = fromMaybe (def k) (M.lookup k mp)- (o,f') <- app -< (elimAutomaton f,i)- returnA -< (o, dispatch0 (M.insert k f' mp) def)+ (o,f') <- app -< (elimMaybeAutomaton f,i)+ case f' of+ Nothing -> returnA -< (o, Nothing)+ Just f' -> returnA -< (o, Just $ fromMaybeAutomaton $ dispatch0 (M.insert k (toMaybeAutomaton f') mp) def) --Utility functions
Control/Arrow/Transformer/LabeledArrow.hs view
@@ -87,15 +87,24 @@ LabeledArrow $ (pushId 0 f *** pushId 1 g) first f = f *** (arr id) -instance (Arrow a, Arrow a', ArrowAddAutomaton a a') =>- ArrowAddAutomaton (LabeledArrow a) (LabeledArrow a') where+instance (Arrow a, Arrow a', ArrowAddAutomaton a may a') =>+ ArrowAddAutomaton (LabeledArrow a) (LabeledArrow may) (LabeledArrow a') where liftAutomaton (LabeledArrow f) = LabeledArrow $ liftAutomaton (f >>> second (arr unLA)) + liftMaybeAutomaton (LabeledArrow f) =+ LabeledArrow $ liftMaybeAutomaton (f >>> second (arr (fmap unLA)))+ elimAutomaton (LabeledArrow f) = LabeledArrow $ elimAutomaton f >>> second (arr LabeledArrow) + elimMaybeAutomaton (LabeledArrow f) =+ LabeledArrow $ elimMaybeAutomaton f + >>> second (arr (fmap LabeledArrow))+ constantAutomaton = LabeledArrow . constantAutomaton . unLA+ toMaybeAutomaton = LabeledArrow . toMaybeAutomaton . unLA+ fromMaybeAutomaton = LabeledArrow . fromMaybeAutomaton . unLA instance (ArrowError ex a) => (ArrowError ex (LabeledArrow a)) where raise = LabeledArrow raise
Web/Horse/Forms.hs view
@@ -21,16 +21,6 @@ import Debug.Trace -valForm- :: (ArrowReader FormIn a',- ArrowAddLabel a a',- ArrowApply a'1,- ArrowAddAutomaton a' a'1,- ArrowChoice a') =>- String- -> a' String (Either String a1)- -> String- -> a () (Html (), Maybe a1) valForm initVal vtor label = withInput $ proc ((),nm,fi) -> do s_curr <- keepState initVal -< fi@@ -45,6 +35,11 @@ readForm = valForm "" (arr (\x -> maybe (Left ("No read: " ++ x)) Right (readMay x))) +enumForm+ :: (ArrowAddAutomaton a1 may a',+ ArrowAddLabel a may,+ ArrowReader FormIn may) =>+ String -> [(String, b)] -> a () (Html (), Maybe b) enumForm label vs = withInput $ (proc ((),nm,fi) -> do n_curr <- keepState (-1) -< extractNumber fi@@ -65,7 +60,10 @@ Nothing -> returnA -< Nothing filterDiffs- :: (Eq i, ArrowApply a', ArrowAddAutomaton a a') => a i (Maybe i)+ :: (ArrowAddAutomaton a may a',+ Eq i,+ ArrowApply a') =>+ a i (Maybe i) filterDiffs = monadToAuto $ \i1 -> do i2 <- co (Just i1) runFilter i1 i2@@ -75,25 +73,30 @@ True -> runFilter i1 =<< co Nothing False -> runFilter i2 =<< co (Just i2) -keepState :: (ArrowApply a', ArrowAddAutomaton a a') => o -> a (Maybe o) o keepState s0 = monadToAuto (f s0) where f s0' ms1 = f (fromMaybe s0' ms1) =<< co (fromMaybe s0' ms1) +{- replaceSecond :: (ArrowAddAutomaton a a', ArrowApply a') => a i o -> a (i, Maybe (a i o)) o+-} replaceSecond g = liftAutomaton $ (proc (i,g_new) -> do let g_curr = elimAutomaton (fromMaybe g g_new) (o,g') <- g_curr -<< i returnA -< (o, replaceSecond g')) +{- once :: (ArrowAddAutomaton a a', ArrowApply a') => a1 -> a i (Maybe a1)+-} once x = monadToAuto $ \_ -> co (Just x) >> forever (co (Nothing)) +{- auto :: Automaton a i o -> a i (o, Automaton a i o)+-} auto (Automaton f) = f @@ -101,13 +104,22 @@ formSum _ [] _ = error "formSum requires at least one argument" -formSum label fs def = catchAuto $ proc _ -> do+formSum label fs def = catchMayAuto $ proc _ -> do (fo,f) <- enumForm label fs -< () case f of- Just f' -> throwAuto -< f'+ Just f' -> throwMayAuto -< f' Nothing -> returnA -< setFormOut fo def +{-+throwMayAuto = proc f -> do+ raise -< liftMaybeAutomaton $ LabeledArrow $+ (arr (flip (,) noInput)+ >>> (unLA (newReader (elimMaybeAutomaton f))))+-}++throwMayAuto = arr (fromMaybeAutomaton) >>> throwAuto + throwAuto = proc f -> do raise -< liftAutomaton $ LabeledArrow $ (arr (flip (,) noInput) @@ -115,7 +127,7 @@ linkForm linkName f = withInput $ proc ((),nm,iname) -> do case iname of- Just _ -> throwAuto -< f+ Just _ -> throwMayAuto -< f Nothing -> returnA -< (link linkName nm)
Web/Horse/Forms/Types.hs view
@@ -1,9 +1,10 @@ {-#LANGUAGE Arrows, TypeSynonymInstances, FlexibleInstances,- FlexibleContexts #-}+ FlexibleContexts, NoMonomorphismRestriction #-} module Web.Horse.Forms.Types where import Control.Arrow.Transformer.Automaton.Monad+import Control.Arrow.Transformer.Automaton.Maybe import Control.Arrow.Transformer.LabeledArrow import Text.Hamlet import Debug.Trace@@ -16,14 +17,18 @@ newtype FormIn = FormIn [(String,String)] deriving (Show) type HoH i o = LabeledArrow (ReaderArrow FormIn (Automaton (Kleisli IO))) i o+type HoHMay i o = LabeledArrow (ReaderArrow FormIn (MaybeAutomaton (Kleisli IO))) i o -type WithError ex i o = LabeledArrow (ErrorArrow ex (ReaderArrow FormIn (Automaton (Kleisli IO)))) i o+type HoHErr ex i o = LabeledArrow (ErrorArrow ex (ReaderArrow FormIn (Automaton (Kleisli IO)))) i o+type HoHErrMay ex i o = LabeledArrow (ErrorArrow ex (ReaderArrow FormIn (MaybeAutomaton (Kleisli IO)))) i o + noInput :: FormIn noInput = FormIn [] filterPrefix :: String -> FormIn -> FormIn-filterPrefix s (FormIn xss) = FormIn $ filter ((== s) . fst) xss+filterPrefix s (FormIn xss) = trace (show (s, xss)) $+ FormIn $ filter ((== s) . fst) xss class HasFormOut o where getFormOut :: o -> FormOut@@ -46,17 +51,29 @@ getSingle (FormIn [(_,x)]) = Just x getSingle _ = Nothing -withInput :: (ArrowReader FormIn a', ArrowAddLabel a a')++withInput+ :: (ArrowReader FormIn a',+ ArrowAddLabel a a',+ ArrowAddAutomaton a1 a' a'1) =>+ a1 (e, String, Maybe String) b -> a e b+withInput = withInput0 . restify++withInput0 :: (ArrowReader FormIn a', + ArrowAddLabel a a') => a' (e,String,Maybe String) b -> a e b-withInput f = runLabel $ proc (e,lab) -> do+withInput0 f = runLabel $ proc (e,lab) -> do fi <- readState -< () f -< (e,show lab,getSingle $ filterPrefix (show lab) fi) +restify g = liftMaybeAutomaton $ proc (e,lab,inp) -> do+ (o,g') <- elimAutomaton g -< (e,lab,inp)+ case inp of+ Nothing -> returnA -< (o,Nothing)+ _ -> returnA -< (o, Just g')+ catchAuto- :: (ArrowAddAutomaton a a', - ArrowApply a',- ArrowChoice a', - ArrowChoice a) =>+ :: (ArrowAddAutomaton a may a') => (LabeledArrow (ErrorArrow (LabeledArrow a i o) a)) i o -> LabeledArrow a i o catchAuto f = liftAutomaton $@@ -65,6 +82,18 @@ >>> second (arr catchAuto)) `elimError` (LabeledArrow $ proc (i,f') -> app -< (unLA (elimAutomaton f'), i))++catchMayAuto+ :: (ArrowAddAutomaton a may a') =>+ LabeledArrow (ErrorArrow (LabeledArrow a t1 o) may) t1 o+ -> LabeledArrow may t1 o+catchMayAuto f = liftMaybeAutomaton $+ (LabeledArrow $+ unLA (elimMaybeAutomaton f)+ >>> second (arr (fmap (fromMaybeAutomaton . catchMayAuto . toMaybeAutomaton) ))) `elimError`+ (LabeledArrow $ proc (i,f') -> do+ (o,g) <- app -< (elimAutomaton $ unLA f', i)+ returnA -< (o, Just $ LabeledArrow g)) runHamlet :: (Arrow a) => a (x -> y) y runHamlet = arr ($ undefined)
Web/Horse/Hack.hs view
@@ -3,6 +3,10 @@ module Web.Horse.Hack where import Web.Horse.Forms+import Data.Time.Clock+import Data.Maybe+import Data.List+import Data.Function import Text.Hamlet import Web.Horse.Forms.Types import Hack ( Env (..), Response (..) )@@ -16,6 +20,8 @@ import Control.Monad import Data.List.Split (splitOn) import Control.Arrow.Transformer.Automaton+import Control.Arrow.Transformer.Automaton.Maybe+import Control.Arrow.Transformer.Automaton.Monad import Control.Arrow.Transformer.Reader import Hack.Handler.EvHTTP (run) import Control.Arrow.Transformer.LabeledArrow@@ -25,42 +31,58 @@ runHorse f = runHorse1 run (simpleReqResp g) where- g = runReader $ runLabeledArrow f >>> arr renderHtml+ g = runReader $ runLabeledArrow $ f + >>> arr renderHtml runHorse1 :: ((Env -> IO Response) -> IO ()) -> - Automaton (Kleisli IO) Env Response -> IO ()+ MaybeAutomaton (Kleisli IO) Env Response -> IO () runHorse1 runner f = do- mv <- newMVar M.empty+ mv <- newMVar [] runner (runWeb mv f) +sessionTarget = 150+ runWeb- :: MVar (M.Map String (MVar (Automaton (Kleisli IO) Env Response)))- -> Automaton (Kleisli IO) Env Response+ :: MVar [(String, MVar (Automaton (Kleisli IO) Env Response))]+ -> MaybeAutomaton (Kleisli IO) Env Response -> Env -> IO Response runWeb mv f0 req = do- (mv_sess, cookie) <- getSessionMVar mv f0 req- sess <- takeMVar mv_sess - (resp,sess') <- runKleisli (auto sess) req - putMVar mv_sess sess'- return (resp{ headers= cookie ++ headers resp }) + compact mv+ mv_sess <- getSessionMVar mv req+ case mv_sess of+ Just mv_sess -> modifyMVar mv_sess $ \sess -> do+ (x,y) <- (runKleisli (auto sess) req)+ return (y,x)+ Nothing -> do+ (resp,f') <- runKleisli (mAut f0) req+ case f' of+ Nothing -> return resp+ Just f' -> do+ (newSess :: Int) <- abs <$> randomIO+ var <- newMVar f'+ modifyMVar_ mv $ return . ((show newSess,var) :)+ let cookie = ("Set-Cookie", + sessionName ++ "=" ++ show newSess ++ "; path=/")+ print ("Set cookie " ++ show newSess)+ return (resp{ headers=(cookie:headers resp) })+ +compact mv = modifyMVar_ mv $ \lst ->+ case length lst > (2 * sessionTarget) of+ True -> return (take sessionTarget $ nubBy ((==) `on` fst) lst)+ False -> return lst+ getSessionMVar- :: MVar (M.Map String (MVar a))- -> a+ :: MVar [(String, MVar (Automaton (Kleisli IO) Env Response))] -> Env- -> IO (MVar a, [(String, String)])-getSessionMVar mv f0 req = do- let sess = lookup sessionName (cookies req)- mp <- takeMVar mv- case join $ M.lookup <$> sess <*> (Just mp) of- Just val -> putMVar mv mp >> return (val,[])- Nothing -> do- (newSess :: Int) <- abs <$> randomIO- var <- newMVar f0- putMVar mv (M.insert (show newSess) var mp)- return (var, [("Set-Cookie", - sessionName ++ "=" ++ show newSess ++ "; path=/")]) + -> IO (Maybe (MVar (Automaton (Kleisli IO) Env Response)))+getSessionMVar mv req = modifyMVar mv $ \lst -> do+ case lookup sessionName (cookies req) of+ Just sess -> case lookup sess lst of+ Just val -> return ((sess,val):lst, Just val)+ Nothing -> return (lst, Nothing)+ Nothing -> return (lst, Nothing) sessionName :: [Char] sessionName = "HaskellOnAHorse"
on-a-horse.cabal view
@@ -7,16 +7,16 @@ -- The package version. See the Haskell package versioning policy -- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for -- standards guiding when and how versions should be incremented.-Version: 0.1+Version: 0.2 -- A short (one-line) description of the package. Synopsis: "Haskell on a Horse" - A combinatorial web framework -- A longer description of the package.-Description: Please read the introduction at http://haskell.on-a-horse.org+Description: Please read the introduction at on-a-horse.org -- URL for the project homepage or repository.-Homepage: haskell.on-a-horse.org+Homepage: http://haskell.on-a-horse.org -- The license under which the package is released. License: BSD3@@ -52,12 +52,13 @@ Library -- Modules exported by the library. Exposed-modules: Web.Horse, Control.Arrow.Transformer.LabeledArrow,- Control.Arrow.Transformer.Automaton.Monad+ Control.Arrow.Transformer.Automaton.Monad,+ Control.Arrow.Transformer.Automaton.Maybe -- Packages needed in order to build this package. Build-depends: base >= 4 && < 5, safe, split, hamlet, hack, hack-handler-evhttp, hack-contrib, containers, - arrows, mtl, random, bytestring+ arrows, mtl, random, bytestring, time -- Modules not exported by this package. Other-modules: Web.Horse.Forms, Web.Horse.Hack,
tutorial.lhs view
@@ -28,7 +28,7 @@ cabal install on-a-horse -> {-#LANGUAGE Arrows, QuasiQuotes, ScopedTypeVariables #-}+> {-#LANGUAGE Arrows, QuasiQuotes, ScopedTypeVariables, NoMonomorphismRestriction #-} > import Web.Horse > import Control.Applicative > import Control.Arrow@@ -70,7 +70,7 @@ <table class=example><tr><td> -> ex2 :: HoH Url (Html ())+> ex2 :: HoHMay Url (Html ()) > ex2 = proc url -> do > (fo1, oper) <- enumForm "operation" > [("times", (*)),@@ -111,7 +111,7 @@ <table class=example><tr><td> -> ex3 :: HoH Url (Html ())+> ex3 :: HoHMay Url (Html ()) > ex3 = formSum "example to run" [("example 1",ex1),("example 2",ex2)] mempty > >>> arr wrapForm @@ -136,14 +136,14 @@ > %input!type=submit > %br |] > where-> term :: String -> HoH () (FormOut, Maybe Integer)-> term label = catchAuto $ formSum label +> term :: String -> HoHMay () (FormOut, Maybe Integer)+> term label = catchMayAuto $ formSum label > [("number", number label), > ("add",oper label "add" (+)), > ("multiply",oper label "multiply" (*))] (mempty, Nothing) >-> number :: String -> WithError (HoH () (FormOut, Maybe Integer)) -> () (FormOut, Maybe Integer)+> number :: String -> HoHErrMay (HoH () (FormOut, Maybe Integer)) +> () (FormOut, Maybe Integer) > number termLabel = proc () -> do > fo1 <- linkForm "cancel" (term termLabel) -< () > (fo2,x) <- readForm "number" -< ()