on develop help sign in
Merge remote-tracking branch 'origin/queryt-debugging' into fast-weak
commitcff33e1da316d0b8e9a48c04db5eb8b2489a7789 authorRyan Trinkle <ryan.trinkle@gmail.com> dateSun 11 Jun 2017, 23:14 (9 years ago) parent4d29fd74be6b70
8 files changed+347−136
default.nix+2−2 reflex.cabal+23−1 src/Reflex/Query/Base.hs+19−9 test/EventWriterT.hs+31−48 test/QueryT.hs+125−14 test/RequesterT.hs+44−49 test/Test/Run.hs+66 test/hlint.hs+37−13
default.nix+2−2
@@ -4,7 +4,7 @@
44, monad-control, mtl, primitive, ref-tf, semigroupoids
55, semigroups, split, stdenv, stm, syb, template-haskell
66, these, transformers, transformers-compat, prim-uniq
7−, data-default
7+, data-default, filepath, directory, filemanip
88, useTemplateHaskell ? true
99}:
1010mkDerivation {
@@ -23,7 +23,7 @@ mkDerivation {
2323 haskell-src-exts haskell-src-meta
2424 ]);
2525 testHaskellDepends = if ghc.isGhcjs or false then [] else [
26− hlint
26+ hlint filepath directory filemanip
2727 ];
2828 configureFlags = if useTemplateHaskell then [] else [
2929 "-f-use-template-haskell"
reflex.cabal+23−1
@@ -180,7 +180,11 @@ test-suite hlint
180180 type: exitcode-stdio-1.0
181181 main-is: hlint.hs
182182 hs-source-dirs: test
183− build-depends: base, hlint == 1.9.*
183+ build-depends: base
184+ , directory
185+ , filepath
186+ , filemanip
187+ , hlint == 1.9.*
184188 if impl(ghcjs)
185189 buildable: False
186190
@@ -190,6 +194,8 @@ test-suite EventWriterT
190194 hs-source-dirs: test
191195 build-depends: base
192196 , dependent-sum
197+ , lens
198+ , these
193199 , transformers
194200 , reflex
195201 , ref-tf
@@ -201,6 +207,22 @@ test-suite RequesterT
201207 build-depends: base
202208 , dependent-sum
203209 , dependent-map
210+ , lens
211+ , these
212+ , transformers
213+ , reflex
214+ , ref-tf
215+
216+test-suite QueryT
217+ type: exitcode-stdio-1.0
218+ main-is: QueryT.hs
219+ hs-source-dirs: test
220+ build-depends: base
221+ , containers
222+ , dependent-sum
223+ , dependent-map
224+ , lens
225+ , these
204226 , transformers
205227 , reflex
206228 , ref-tf
src/Reflex/Query/Base.hs+19−9
@@ -60,18 +60,28 @@ getQueryTLoweredResultValue (QueryTLoweredResult (v, _)) = v
6060getQueryTLoweredResultWritten :: QueryTLoweredResult t q v -> [Behavior t q]
6161getQueryTLoweredResultWritten (QueryTLoweredResult (_, w)) = w
6262
63+{-
64+let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
65+ sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
66+ bs' = fmapCheap snd $ r'
67+ patches = unsafeBuildIncremental (sampleBs bs0) $
68+ flip pushCheap bs' $ \bs -> do
69+ p <- (~~) <$> sampleBs bs <*> sample (currentIncremental patches)
70+ return (Just (AdditivePatch p))
71+-}
72+
6373instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, MonadAdjust t m) => MonadAdjust t (QueryT t q m) where
6474 runWithReplace (QueryT a0) a' = do
6575 ((r0, bs0), r') <- QueryT $ lift $ runWithReplace (runStateT a0 []) $ fmapCheap (flip runStateT [] . unQueryT) a'
66− tellQueryIncremental $
67− let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
68− sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
69− bs' = fmapCheap snd $ r'
70− patches = unsafeBuildIncremental (sampleBs bs0) $
71− flip pushCheap bs' $ \bs -> do
72− p <- (~~) <$> sampleBs bs <*> sample (currentIncremental patches)
73− return (Just (AdditivePatch p))
74− in patches
76+ let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
77+ sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
78+ bs' = fmapCheap snd $ r'
79+ bbs <- hold bs0 bs'
80+ let patches = flip pushAlwaysCheap bs' $ \newBs -> do
81+ oldBs <- sample bbs
82+ (~~) <$> sampleBs newBs <*> sampleBs oldBs
83+ QueryT $ modify $ (:) $ pull $ sampleBs =<< sample bbs
84+ QueryT $ lift $ tellEvent patches
7585 return (r0, fmapCheap fst r')
7686 traverseDMapWithKeyWithAdjust :: forall (k :: * -> *) v v'. (DMap.GCompare k) => (forall a. k a -> v a -> QueryT t q m (v' a)) -> DMap k v -> Event t (PatchDMap k v) -> QueryT t q m (DMap k v', Event t (PatchDMap k v'))
7787 traverseDMapWithKeyWithAdjust f dm0 dm' = do
test/EventWriterT.hs+31−48
@@ -1,61 +1,44 @@
11{-# LANGUAGE FlexibleContexts, GADTs #-}
22module Main where
33
4+import Control.Lens
45import Control.Monad
5−import Control.Monad.IO.Class
6−import Control.Monad.Ref
7−import Data.Dependent.Sum
8−import Data.IORef
9−import Data.List
10−import Data.Functor.Identity
6+import Data.These
117
128import Reflex
139import Reflex.EventWriter
14−import Reflex.Host.Class
15−import Reflex.PerformEvent.Base
16−
17−import System.Exit (exitFailure)
10+import Test.Run
1811
1912main :: IO ()
2013main = do
21− (Just out1, Nothing) <- runApp app1
22− print out1
23− (Just out2, Just (out3, out4)) <- runApp app2
24− print out2
25− print out3
26− print out4
27− when (sort out1 /= out1 || map (+1) out2 /= out3 || out3 /= out4) exitFailure
14+ os1@[[Just [10,9,8,7,6,5,4,3,2,1]]] <- runApp' (unwrapApp testOrdering) $
15+ [ Just ()
16+ ]
17+ print os1
18+ os2@[[Just [1,3,5,7,9]],[Nothing,Nothing],[Just [2,4,6,8,10]],[Just [2,4,6,8,10],Nothing]]
19+ <- runApp' (unwrapApp testSimultaneous) $ map Just $
20+ [ This ()
21+ , That ()
22+ , This ()
23+ , These () ()
24+ ]
25+ print os2
26+ return ()
27+
28+unwrapApp :: (Reflex t, Monad m) => (a -> EventWriterT t [Int] m ()) -> a -> m (Event t [Int])
29+unwrapApp x appIn = do
30+ ((), e) <- runEventWriterT $ x appIn
31+ return e
2832
29−runApp :: (Show a, t ~ SpiderTimeline Global, m ~ SpiderHost Global)
30− => (Event t () -> EventWriterT t [a] (PerformEventT t m) (Maybe (Ref m (Maybe (EventTrigger t ())))))
31− -> IO (Maybe [a], Maybe ([a], [a]))
32−runApp x = runSpiderHost $ do
33− (pulseE, pulseTriggerRef) <- newEventWithTriggerRef
34− ((mactuateRef, e), FireCommand fire) <- hostPerformEventT $ runEventWriterT (x pulseE)
35− hnd <- subscribeEvent e
36− out1 <- fireEventRefAndRead pulseTriggerRef () hnd
37− out2 <- fmap join $ forM mactuateRef $ \actuateRef -> do
38− let readPhase = do
39− mGetValue <- readEvent hnd
40− case mGetValue of
41− Nothing -> return Nothing
42− Just getValue -> fmap Just getValue
43− mactuate <- readRef actuateRef
44− mpulse <- readRef pulseTriggerRef
45− forM ((,) <$> mactuate <*> mpulse) $ \(actuate, pulse) -> do
46− _ <- fire [actuate :=> Identity ()] $ return ()
47− [Just a] <- fire [pulse :=> Identity ()] $ readPhase
48− [Just b, Nothing] <- fire [actuate :=> Identity (), pulse :=> Identity ()] $ readPhase
49− return (a,b)
50− return (out1, out2)
33+testOrdering :: (Reflex t, Monad m) => Event t () -> EventWriterT t [Int] m ()
34+testOrdering pulse = do
35+ forM_ [10,9..1] $ \i -> tellEvent ([i] <$ pulse)
36+ return ()
5137
52−app1 :: (Reflex t, Ref m ~ IORef, EventWriter t [Int] m) => Event t () -> m (Maybe (Ref m (Maybe (EventTrigger t ()))))
53−app1 e = do
54− forM_ [1..10] $ \i -> tellEvent ([i] <$ e)
55− return Nothing
38+testSimultaneous :: (Reflex t, MonadAdjust t m, MonadHold t m) => Event t (These () ()) -> EventWriterT t [Int] m ()
39+testSimultaneous pulse = do
40+ let e0 = fmapMaybe (^? here) pulse
41+ e1 = fmapMaybe (^? there) pulse
42+ forM_ [1,3..9] $ \i -> runWithReplace (tellEvent ([i] <$ e0)) $ ffor e1 $ \_ -> tellEvent ([i+1] <$ e0)
43+ return ()
5644
57−app2 :: (Reflex t, Ref m ~ IORef, EventWriter t [Int] m, MonadAdjust t m, MonadReflexCreateTrigger t m, MonadRef m) => Event t () -> m (Maybe (Ref m (Maybe (EventTrigger t ()))))
58−app2 e = do
59− (pulse, pulseTriggerRef) <- newEventWithTriggerRef
60− forM_ [1,3..9] $ \i -> runWithReplace (tellEvent ([i] <$ e)) $ ffor pulse $ \_ -> tellEvent ([i+1] <$ e)
61− return (Just pulseTriggerRef)
test/QueryT.hs+125−14
@@ -1,8 +1,25 @@
1+{-# LANGUAGE DeriveFunctor #-}
2+{-# LANGUAGE FlexibleContexts #-}
13{-# LANGUAGE TypeFamilies #-}
24{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3−import qualified Data.AppendMap as Map
5+{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE OverloadedStrings #-}
7+{-# LANGUAGE RankNTypes #-}
8+
9+import Control.Lens
10+import Control.Monad.Fix
11+import Data.Align
12+import Data.AppendMap (AppendMap)
13+import qualified Data.AppendMap as AMap
14+import Data.Functor.Misc
15+import Data.Map (Map)
16+import qualified Data.Map as Map
417import Data.Semigroup
5−import Reflex.Dom
18+import Data.These
19+
20+import Reflex
21+import Reflex.Patch.MapWithMove
22+import Test.Run
623
724newtype MyQuery = MyQuery SelectedCount
825 deriving (Show, Read, Eq, Ord, Monoid, Semigroup, Additive, Group)
@@ -11,19 +28,113 @@ instance Query MyQuery where
1128 type QueryResult MyQuery = ()
1229 crop _ _ = ()
1330
14−instance (Ord k, Query a) => Query (Map.AppendMap k a) where
15− type QueryResult (Map.AppendMap k a) = Map.AppendMap k (QueryResult a)
31+instance (Ord k, Query a, Eq (QueryResult a)) => Query (Selector k a) where
32+ type QueryResult (Selector k a) = Selector k (QueryResult a)
1633 crop q r = undefined
1734
35+newtype Selector k a = Selector { unSelector :: AppendMap k a }
36+ deriving (Show, Read, Eq, Ord, Functor)
37+
38+instance (Ord k, Eq a, Monoid a) => Semigroup (Selector k a) where
39+ (Selector a) <> (Selector b) = Selector $ fmapMaybe id $ f a b
40+ where
41+ f = alignWith $ \case
42+ This x -> Just x
43+ That y -> Just y
44+ These x y ->
45+ let z = x `mappend` y
46+ in if z == mempty then Nothing else Just z
47+
48+instance (Ord k, Eq a, Monoid a) => Monoid (Selector k a) where
49+ mempty = Selector AMap.empty
50+ mappend = (<>)
51+
52+instance (Eq a, Ord k, Group a) => Group (Selector k a) where
53+ negateG = fmap negateG
54+
55+instance (Eq a, Ord k, Group a) => Additive (Selector k a)
56+
1857main :: IO ()
19−main = mainWidget $ do
58+main = do
59+ [0, 1, 1, 0] <- fmap (map fst . concat) $
60+ runApp (testQueryT testRunWithReplace) () $ map (Just . That) $
61+ [ That (), This (), That () ]
62+ [0, 1, 1, 0] <- fmap (map fst . concat) $
63+ runApp (testQueryT testSequenceDMapWithAdjust) () $ map (Just . That) $
64+ [ That (), This (), That () ]
65+ [0, 1, 1, 0] <- fmap (map fst . concat) $
66+ runApp (testQueryT testSequenceDMapWithAdjustWithMove) () $ map (Just . That) $
67+ [ That (), This (), That () ]
68+ return ()
69+
70+testQueryT :: (Reflex t, MonadFix m)
71+ => (Event t () -> Event t () -> QueryT t (Selector Int MyQuery) m ())
72+ -> AppIn t () (These () ())
73+ -> m (AppOut t Int Int)
74+testQueryT w (AppIn _ pulse) = do
75+ let replace = fmapMaybe (^? here) pulse
76+ increment = fmapMaybe (^? there) pulse
77+ (_, q) <- runQueryT (w replace increment) $ pure mempty
78+ let qDyn = head . AMap.keys . unSelector <$> incrementalToDynamic q
79+ return $ AppOut
80+ { _appOut_behavior = current qDyn
81+ , _appOut_event = updated qDyn
82+ }
83+
84+testRunWithReplace :: ( Reflex t
85+ , MonadAdjust t m
86+ , MonadHold t m
87+ , MonadFix m
88+ , MonadQuery t (Selector Int MyQuery) m)
89+ => Event t ()
90+ -> Event t ()
91+ -> m ()
92+testRunWithReplace replace increment = do
2093 let w = do
21− n <- count =<< button "Increment"
22− display n
23− queryDyn $ zipDynWith Map.singleton n $ pure $ MyQuery $ SelectedCount 1
24− return ()
25− outer = do
26− replace <- button "Replace"
27− widgetHold w $ w <$ replace
28− (_, q) <- runQueryT outer $ pure mempty
29− display $ incrementalToDynamic q
94+ n <- count increment
95+ queryDyn $ zipDynWith (\x y -> Selector (AMap.singleton (x :: Int) y)) n $ pure $ MyQuery $ SelectedCount 1
96+ _ <- runWithReplace w $ w <$ replace
97+ return ()
98+
99+testSequenceDMapWithAdjust :: ( Reflex t
100+ , MonadAdjust t m
101+ , MonadHold t m
102+ , MonadFix m
103+ , MonadQuery t (Selector Int MyQuery) m)
104+ => Event t ()
105+ -> Event t ()
106+ -> m ()
107+testSequenceDMapWithAdjust replace increment = do
108+ _ <- listHoldWithKey (Map.singleton () ()) (Map.singleton () (Just ()) <$ replace) $ \_ _ -> do
109+ n <- count increment
110+ queryDyn $ zipDynWith (\x y -> Selector (AMap.singleton (x :: Int) y)) n $ pure $ MyQuery $ SelectedCount 1
111+ return ()
112+
113+testSequenceDMapWithAdjustWithMove :: ( Reflex t
114+ , MonadAdjust t m
115+ , MonadHold t m
116+ , MonadFix m
117+ , MonadQuery t (Selector Int MyQuery) m)
118+ => Event t ()
119+ -> Event t ()
120+ -> m ()
121+testSequenceDMapWithAdjustWithMove replace increment = do
122+ _ <- listHoldWithKeyWithMove (Map.singleton () ()) (Map.singleton () (Just ()) <$ replace) $ \_ _ -> do
123+ n <- count increment
124+ queryDyn $ zipDynWith (\x y -> Selector (AMap.singleton (x :: Int) y)) n $ pure $ MyQuery $ SelectedCount 1
125+ return ()
126+
127+listHoldWithKey :: forall t m k v a. (Ord k, MonadHold t m, MonadAdjust t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
128+listHoldWithKey m0 m' f = do
129+ let dm0 = mapWithFunctorToDMap $ Map.mapWithKey f m0
130+ dm' = fmap (PatchDMap . mapWithFunctorToDMap . Map.mapWithKey (\k v -> ComposeMaybe $ fmap (f k) v)) m'
131+ (a0, a') <- sequenceDMapWithAdjust dm0 dm'
132+ fmap dmapToMap . incrementalToDynamic <$> holdIncremental a0 a'
133+
134+
135+-- scam it out to test traverseDMapWithAdjustWithMove
136+listHoldWithKeyWithMove :: forall t m k v a. (Ord k, MonadHold t m, MonadAdjust t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
137+listHoldWithKeyWithMove m0 m' f = do
138+ (n0, n') <- mapMapWithAdjustWithMove f m0 $ ffor m' $ PatchMapWithMove . Map.map (\v -> NodeInfo (maybe From_Delete From_Insert v) Nothing)
139+ incrementalToDynamic <$> holdIncremental n0 n'
140+-- -}
test/RequesterT.hs+44−49
@@ -1,70 +1,65 @@
1−{-# LANGUAGE FlexibleContexts, GADTs #-}
1+{-# LANGUAGE FlexibleContexts, GADTs, RankNTypes, ScopedTypeVariables #-}
22module Main where
33
4+import Control.Lens
45import Control.Monad
5−import Control.Monad.IO.Class
6−import Control.Monad.Ref
76import qualified Data.Dependent.Map as DMap
87import Data.Dependent.Sum
9−import Data.IORef
10−import Data.List
11−import Data.Functor.Identity
8+import Data.These
129
1310import Reflex
14−import Reflex.Host.Class
15−import Reflex.PerformEvent.Base
1611import Reflex.Requester.Base
1712import Reflex.Requester.Class
18−
19−import System.Exit (exitFailure, exitSuccess)
13+import Test.Run
2014
2115data RequestInt a where
2216 RequestInt :: Int -> RequestInt Int
2317
2418main :: IO ()
2519main = do
26− (Just out1, Nothing) <- runApp app1
27− print out1
28− (Just out2, Just (out3, out4)) <- runApp app2
29− print out2
30− print out3
31− print out4
32− when (sort out1 /= out1 || map (+1) out2 /= out3 || out3 /= out4) exitFailure
33−
34−runApp :: (t ~ SpiderTimeline Global, m ~ SpiderHost Global)
35− => (Event t () -> RequesterT t RequestInt Identity (PerformEventT t m) (Maybe (Ref m (Maybe (EventTrigger t ())))))
36− -> IO (Maybe [Int], Maybe ([Int], [Int]))
37−runApp x = runSpiderHost $ do
38− (pulseE, pulseTriggerRef) <- newEventWithTriggerRef
39− ((mactuateRef, e), FireCommand fire) <- hostPerformEventT $ runRequesterT (x pulseE) never
40− hnd <- subscribeEvent (map unwrapRequest . DMap.toList <$> e)
41− out1 <- fireEventRefAndRead pulseTriggerRef () hnd
42− out2 <- fmap join $ forM mactuateRef $ \actuateRef -> do
43− let readPhase = do
44− mGetValue <- readEvent hnd
45− case mGetValue of
46− Nothing -> return Nothing
47− Just getValue -> fmap Just getValue
48− mactuate <- readRef actuateRef
49− mpulse <- readRef pulseTriggerRef
50− forM ((,) <$> mactuate <*> mpulse) $ \(actuate, pulse) -> do
51− _ <- fire [actuate :=> Identity ()] $ return ()
52− [Just a] <- fire [pulse :=> Identity ()] $ readPhase
53− [Just b, Nothing] <- fire [actuate :=> Identity (), pulse :=> Identity ()] $ readPhase
54− return (a,b)
55− return (out1, out2)
20+ os1@[[Just [10,9,8,7,6,5,4,3,2,1]]] <- runApp' (unwrapApp testOrdering) $
21+ [ Just ()
22+ ]
23+ print os1
24+ os2@[[Just [1,3,5,7,9]],[Nothing,Nothing],[Just [2,4,6,8,10]],[Just [2,4,6,8,10],Nothing]]
25+ <- runApp' (unwrapApp testSimultaneous) $ map Just $
26+ [ This ()
27+ , That ()
28+ , This ()
29+ , These () ()
30+ ]
31+ print os2
32+ return ()
5633
5734unwrapRequest :: DSum tag RequestInt -> Int
5835unwrapRequest (_ :=> RequestInt i) = i
5936
60−app1 :: (Reflex t, Ref m ~ IORef, Requester t m, Response m ~ Identity, Request m ~ RequestInt) => Event t () -> m (Maybe (Ref m (Maybe (EventTrigger t ()))))
61−app1 e = do
62− forM_ [1..10] $ \i -> requestingIdentity (RequestInt i <$ e)
63− return Nothing
37+unwrapApp :: ( Reflex t, Monad m )
38+ => (a -> RequesterT t RequestInt Identity m ())
39+ -> a
40+ -> m (Event t [Int])
41+unwrapApp x appIn = do
42+ ((), e) <- runRequesterT (x appIn) never
43+ return $ fmap (map unwrapRequest . DMap.toList) e
6444
65−app2 :: (Reflex t, Ref m ~ IORef, Requester t m, Response m ~ Identity, Request m ~ RequestInt, MonadAdjust t m, MonadReflexCreateTrigger t m, MonadRef m) => Event t () -> m (Maybe (Ref m (Maybe (EventTrigger t ()))))
66−app2 e = do
67− (pulse, pulseTriggerRef) <- newEventWithTriggerRef
68− forM_ [1,3..9] $ \i -> runWithReplace (requestingIdentity (RequestInt i <$ e)) $ ffor pulse $ \_ -> requestingIdentity (RequestInt (i+1) <$ e)
69− return (Just pulseTriggerRef)
45+testOrdering :: ( Response m ~ Identity
46+ , Request m ~ RequestInt
47+ , Requester t m
48+ , MonadAdjust t m)
49+ => Event t ()
50+ -> m ()
51+testOrdering pulse = do
52+ forM_ [10,9..1] $ \i -> requestingIdentity (RequestInt i <$ pulse)
53+ return ()
7054
55+testSimultaneous :: ( Response m ~ Identity
56+ , Request m ~ RequestInt
57+ , Requester t m
58+ , MonadAdjust t m)
59+ => Event t (These () ())
60+ -> m ()
61+testSimultaneous pulse = do
62+ let tellE = fmapMaybe (^? here) pulse
63+ switchE = fmapMaybe (^? there) pulse
64+ forM_ [1,3..9] $ \i -> runWithReplace (requestingIdentity (RequestInt i <$ tellE)) $ ffor switchE $ \_ ->
65+ requestingIdentity (RequestInt (i+1) <$ tellE)
test/Test/Run.hsnew file+66
@@ -0,0 +1,66 @@
1+{-# LANGUAGE GADTs #-}
2+{-# LANGUAGE LambdaCase #-}
3+module Test.Run where
4+
5+import Control.Monad
6+import Control.Monad.Ref
7+import Data.Dependent.Sum
8+import Data.Functor.Identity
9+import Data.These
10+
11+import Reflex
12+import Reflex.Host.Class
13+
14+data AppIn t b e = AppIn
15+ { _appIn_behavior :: Behavior t b
16+ , _appIn_event :: Event t e
17+ }
18+
19+data AppOut t b e = AppOut
20+ { _appOut_behavior :: Behavior t b
21+ , _appOut_event :: Event t e
22+ }
23+
24+runApp :: (t ~ SpiderTimeline Global, m ~ SpiderHost Global)
25+ => (AppIn t bIn eIn -> PerformEventT t m (AppOut t bOut eOut))
26+ -> bIn
27+ -> [Maybe (These bIn eIn)]
28+ -> IO [[(bOut, Maybe eOut)]]
29+runApp app b0 input = runSpiderHost $ do
30+ (appInHoldE, pulseHoldTriggerRef) <- newEventWithTriggerRef
31+ (appInE, pulseEventTriggerRef) <- newEventWithTriggerRef
32+ appInB <- hold b0 appInHoldE
33+ (out, FireCommand fire) <- hostPerformEventT $ app $ AppIn
34+ { _appIn_event = appInE
35+ , _appIn_behavior = appInB
36+ }
37+ hnd <- subscribeEvent (_appOut_event out)
38+ mpulseB <- readRef pulseHoldTriggerRef
39+ mpulseE <- readRef pulseEventTriggerRef
40+ let readPhase = do
41+ b <- sample (_appOut_behavior out)
42+ frames <- sequence =<< readEvent hnd
43+ return (b, frames)
44+ forM input $ \case
45+ Nothing ->
46+ fire [] $ readPhase
47+ Just i -> case i of
48+ This b' -> case mpulseB of
49+ Nothing -> error "tried to fire in-behavior but ref was empty"
50+ Just pulseB -> fire [ pulseB :=> Identity b' ] $ readPhase
51+ That e' -> case mpulseE of
52+ Nothing -> error "tried to fire in-event but ref was empty"
53+ Just pulseE -> fire [ pulseE :=> Identity e' ] $ readPhase
54+ These b' e' -> case mpulseB of
55+ Nothing -> error "tried to fire in-behavior but ref was empty"
56+ Just pulseB -> case mpulseE of
57+ Nothing -> error "tried to fire in-event but ref was empty"
58+ Just pulseE -> fire [ pulseB :=> Identity b', pulseE :=> Identity e' ] $ readPhase
59+
60+runApp' :: (t ~ SpiderTimeline Global, m ~ SpiderHost Global)
61+ => (Event t eIn -> PerformEventT t m (Event t eOut))
62+ -> [Maybe eIn]
63+ -> IO [[Maybe eOut]]
64+runApp' app input = do
65+ let app' = fmap (AppOut (pure ())) . app
66+ map (map snd) <$> runApp (app' . _appIn_event) () (map (fmap That) input)
test/hlint.hs+37−13
@@ -1,19 +1,43 @@
1+module Main where
2+
3+import Control.Monad
14import Language.Haskell.HLint3 (hlint)
5+import System.Directory
26import System.Exit (exitFailure, exitSuccess)
7+import System.FilePath.Find
8+import System.FilePath
39
410main :: IO ()
511main = do
6− ideas <- hlint
7− [ "."
8− , "--ignore=Redundant do"
9− , "--ignore=Use camelCase"
10− , "--ignore=Redundant $"
11− , "--ignore=Use &&"
12− , "--ignore=Use &&&"
13− , "--ignore=Use const"
14− , "--ignore=Use >=>"
15− , "--ignore=Use ."
16− , "--ignore=Use unless"
17− , "--cpp-define=USE_TEMPLATE_HASKELL"
18− ]
12+ pwd <- getCurrentDirectory
13+ let runHlint f = hlint $ f:
14+ [ "--ignore=Redundant do"
15+ , "--ignore=Use camelCase"
16+ , "--ignore=Redundant $"
17+ , "--ignore=Use &&"
18+ , "--ignore=Use &&&"
19+ , "--ignore=Use const"
20+ , "--ignore=Use >=>"
21+ , "--ignore=Use ."
22+ , "--ignore=Use unless"
23+ , "--cpp-define=USE_TEMPLATE_HASKELL"
24+ ]
25+ recurseInto = and <$> sequence
26+ [ fileType ==? Directory
27+ , fileName /=? ".git"
28+ ]
29+ matchFile = and <$> sequence
30+ [ extension ==? ".hs"
31+ , let notElem' = liftOp notElem
32+ in filePath `notElem'` filePathExceptions pwd
33+ ]
34+ files <- find recurseInto matchFile pwd
35+ ideas <- fmap concat $ forM files $ \f -> do
36+ putStr $ "linting file " ++ drop (length pwd + 1) f ++ "... "
37+ runHlint f
1938 if null ideas then exitSuccess else exitFailure
39+
40+filePathExceptions :: FilePath -> [FilePath]
41+filePathExceptions pwd = map (pwd </>) $
42+ [ "src/Data/AppendMap.hs" -- parse error when hlint runs
43+ ]