on develop help sign in
Merge pull request #69 from lspitzner/develop
50d298cRyan Trinkle
.gitignore+2
@@ -1,4 +1,6 @@
1dist
1dist
2+cabal.sandbox.config
3+.cabal-sandbox/
2cabal-dev
4cabal-dev
3*.o
5*.o
4*.hi
6*.hi
Quickref.md+17−10
@@ -48,17 +48,22 @@ Since MonadHold depends on MonadSample, any [S] function also runs in [H] contex
48[ ] attachPromptlyDynWithMaybe :: (a -> b -> Maybe c) -> Dynamic a -> Event b -> Event c
48[ ] attachPromptlyDynWithMaybe :: (a -> b -> Maybe c) -> Dynamic a -> Event b -> Event c
49
49
50-- Combine multiple Events
50-- Combine multiple Events
51−[ ] <> :: Monoid a => Event a -> Event a -> Event a
51+[ ] <> :: Semigroup a => Event a -> Event a -> Event a
52+[ ] difference :: Event a -> Event b -> Event a
53+[ ] align :: Event a -> Event b -> Event (These a b)
54+[ ] alignWith :: (These a b -> c) -> Event a -> Event b -> Event c
52−[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a
55+[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a
53−[ ] leftmost :: [Event a] -> Event a
56+[ ] leftmost :: [Event a] -> Event a
54−[ ] mergeList :: [Event a] -> Event (NonEmpty a)
57+[ ] mergeList :: [Event a] -> Event (NonEmpty a)
55−[ ] merge :: GCompare k => DMap (WrapArg Event k) -> Event (DMap k)
58+[ ] merge :: GCompare k => DMap (WrapArg Event k) -> Event (DMap k)
56−[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a)
59+[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a)
57
60
58-- Efficient one-to-many fanout
61-- Efficient one-to-many fanout
59−[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
62+[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
60−[ ] fan :: GCompare k => Event (DMap k) -> EventSelector k
63+[ ] fan :: GCompare k => Event (DMap k) -> EventSelector k
61−[ ] select :: EventSelector k -> k a -> Event a
64+[ ] select :: EventSelector k -> k a -> Event a
65+[ ] fanEither :: Event (Either a b) -> (Event a, Event b)
66+[ ] fanThese :: Event (These a b) -> (Event a, Event b)
62
67
63-- Event to Event via function that can sample current values
68-- Event to Event via function that can sample current values
64[ ] push :: (a -> m (Maybe b)) -> Event a -> Event b
69[ ] push :: (a -> m (Maybe b)) -> Event a -> Event b
@@ -124,7 +129,9 @@ Since MonadHold depends on MonadSample, any [S] function also runs in [H] contex
124-- Combine multiple Dynamics
129-- Combine multiple Dynamics
125[ ] mconcat :: Monoid a => [Dynamic a] -> Dynamic a
130[ ] mconcat :: Monoid a => [Dynamic a] -> Dynamic a
126[ ] distributeDMapOverDynPure :: GCompare k => DMap (WrapArg Dynamic k) -> Dynamic (DMap k)
131[ ] distributeDMapOverDynPure :: GCompare k => DMap (WrapArg Dynamic k) -> Dynamic (DMap k)
132+[ ] <*> :: Dynamic (a -> b) -> Dynamic a -> Dynamic b
133+[ ] >>= :: Dynamic a -> (a -> Dynamic b) -> Dynamic b
127−[ ] zipDynWith :: (a -> b -> c) -> Dynamic a -> Dynamic b -> Dynamic c
134+[ ] zipDynWith :: (a -> b -> c) -> Dynamic a -> Dynamic b -> Dynamic c
128
135
129-- Efficient one-to-many fanout
136-- Efficient one-to-many fanout
130[ ] demux :: Ord k => Dynamic k -> Demux k
137[ ] demux :: Ord k => Dynamic k -> Demux k
src/Reflex/Class.hs+32−2
@@ -34,6 +34,7 @@ module Reflex.Class
34 , mergeMap
34 , mergeMap
35 , mergeList
35 , mergeList
36 , mergeWith
36 , mergeWith
37+ , difference
37 -- ** Breaking up 'Event's
38 -- ** Breaking up 'Event's
38 , splitE
39 , splitE
39 , fanEither
40 , fanEither
@@ -408,8 +409,21 @@ instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
408#endif
409#endif
409
410
410--TODO: See if there's a better class in the standard libraries already
411--TODO: See if there's a better class in the standard libraries already
412+
411-- | A class for values that combines filtering and mapping using 'Maybe'.
413-- | A class for values that combines filtering and mapping using 'Maybe'.
414+-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@. Also similar is the
415+-- @Witherable@ typeclass, but it requires @Foldable f@ and @Traverable f@,
416+-- and e.g. 'Event' is instance of neither.
417+--
418+-- A definition of 'fmapMaybe' must satisfy the following laws:
419+--
420+-- [/identity/]
421+-- @'fmapMaybe' 'Just' ≡ 'id'@
422+--
423+-- [/composition/]
424+-- @'fmapMaybe' (f <=< g) ≡ 'fmapMaybe' f . 'fmapMaybe' g@
425+
412−class FunctorMaybe f where
426+class Functor f => FunctorMaybe f where
413 -- | Combined mapping and filtering function.
427 -- | Combined mapping and filtering function.
414 fmapMaybe :: (a -> Maybe b) -> f a -> f b
428 fmapMaybe :: (a -> Maybe b) -> f a -> f b
415
429
@@ -640,7 +654,7 @@ switchPromptly ea0 eea = do
640
654
641instance Reflex t => Align (Event t) where
655instance Reflex t => Align (Event t) where
642 nil = never
656 nil = never
643− align ea eb = fmapMaybe dmapToThese $ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
657+ align = alignWithMaybe Just
644
658
645-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
659-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
646-- the 'Behavior' is true at the time of occurence.
660-- the 'Behavior' is true at the time of occurence.
@@ -660,6 +674,7 @@ zipDyn = zipDynWith (,)
660
674
661-- | Combine two 'Dynamic's with a combining function. The result will change
675-- | Combine two 'Dynamic's with a combining function. The result will change
662-- whenever either (or both) input 'Dynamic' changes.
676-- whenever either (or both) input 'Dynamic' changes.
677+-- More efficient than liftA2.
663zipDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> Dynamic t c
678zipDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> Dynamic t c
664zipDynWith f da db =
679zipDynWith f da db =
665 let eab = align (updated da) (updated db)
680 let eab = align (updated da) (updated db)
@@ -703,6 +718,21 @@ distributeListOverDyn = distributeListOverDynWith id
703distributeListOverDynWith :: Reflex t => ([a] -> b) -> [Dynamic t a] -> Dynamic t b
718distributeListOverDynWith :: Reflex t => ([a] -> b) -> [Dynamic t a] -> Dynamic t b
704distributeListOverDynWith f = fmap (f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) . distributeDMapOverDynPure . DMap.fromList . map (\(k, v) -> Const2 k :=> v) . zip [0 :: Int ..]
719distributeListOverDynWith f = fmap (f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) . distributeDMapOverDynPure . DMap.fromList . map (\(k, v) -> Const2 k :=> v) . zip [0 :: Int ..]
705
720
721+-- | Create a new 'Event' that occurs when the first supplied 'Event' occurs
722+-- unless the second supplied 'Event' occurs simultaneously.
723+difference :: Reflex t => Event t a -> Event t b -> Event t a
724+difference = alignWithMaybe $ \case
725+ This a -> Just a
726+ _ -> Nothing
727+
728+-- (intentially not exported, for now)
729+alignWithMaybe
730+ :: Reflex t => (These a b -> Maybe c) -> Event t a -> Event t b -> Event t c
731+alignWithMaybe f ea eb =
732+ fmapMaybe (f <=< dmapToThese)
733+ $ merge
734+ $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
735+
706
736
707--------------------------------------------------------------------------------
737--------------------------------------------------------------------------------
708-- Accumulator
738-- Accumulator
src/Reflex/Spider/Internal.hs+3−3
@@ -840,10 +840,10 @@ instance Functor (Dynamic Identity) where
840 fmap = newMapDyn
840 fmap = newMapDyn
841
841
842instance Applicative (Dynamic Identity) where
842instance Applicative (Dynamic Identity) where
843− pure = DynamicConst
843+ pure = DynamicConst
844 (<*>) = zipDynWith ($)
844 (<*>) = zipDynWith ($)
845− _ *> b = b
845+ (*>) = zipDynWith (flip const)
846− a <* _ = a
846+ (<*) = zipDynWith const
847
847
848instance R.FunctorMaybe Event where
848instance R.FunctorMaybe Event where
849 fmapMaybe f = push $ return . f
849 fmapMaybe f = push $ return . f
test/Reflex/Test/Micro.hs+5
@@ -242,6 +242,11 @@ testCases =
242 f <- fanMap . fmap toMap <$> events1
242 f <- fanMap . fmap toMap <$> events1
243 return $ toList <$> mergeList [ select f (Const2 'b'), select f (Const2 'b'), select f (Const2 'e'), select f (Const2 'e') ]
243 return $ toList <$> mergeList [ select f (Const2 'b'), select f (Const2 'b'), select f (Const2 'e'), select f (Const2 'e') ]
244
244
245+ , testE "difference" $ do
246+ e1 <- events1
247+ e2 <- events2
248+ return $ e1 `difference ` e2
249+
245 ] where
250 ] where
246
251
247 events1, events2, events3 :: TestPlan t m => m (Event t String)
252 events1, events2, events3 :: TestPlan t m => m (Event t String)