@@ -34,6 +34,7 @@ module Reflex.Class
34 , mergeMap
34 , mergeMap
35 , mergeList
35 , mergeList
36 , mergeWith
36 , mergeWith
37 -- ** Breaking up 'Event's
38 -- ** Breaking up 'Event's
39 , fanEither
40 , fanEither
@@ -408,8 +409,21 @@ instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
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
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.
418+-- A definition of 'fmapMaybe' must satisfy the following laws:
421+-- @'fmapMaybe' 'Just' ≡ 'id'@
424+-- @'fmapMaybe' (f <=< g) ≡ 'fmapMaybe' f . 'fmapMaybe' g@
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
@@ -640,7 +654,7 @@ switchPromptly ea0 eea = do
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
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 (,)
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 ..]
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
728+-- (intentially not exported, for now)
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)
734+ $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
707--------------------------------------------------------------------------------
737--------------------------------------------------------------------------------
708-- Accumulator
738-- Accumulator