@@ -3,11 +3,11 @@
3{-# LANGUAGE MultiParamTypeClasses #-}
3{-# LANGUAGE MultiParamTypeClasses #-}
4-- | This module provides a variation of 'Dynamic' values that uses cheap
4-- | This module provides a variation of 'Dynamic' values that uses cheap
5-- pointer equality checks to reduce the amount of signal propagation needed.
5-- pointer equality checks to reduce the amount of signal propagation needed.
6−module Reflex.Dynamic.Nub
6+module Reflex.Dynamic.Uniq
7− ( NubDynamic
7+ ( UniqDynamic
8− , nubDynamic
8+ , uniqDynamic
9− , fromNubDynamic
9+ , fromUniqDynamic
10− , alreadyNubbedDynamic
10+ , alreadyUniqDynamic
13import Control.Applicative (Applicative (..))
13import Control.Applicative (Applicative (..))
@@ -17,32 +17,33 @@ import Reflex.Dynamic
18-- | A 'Dynamic' whose 'updated' 'Event' will never fire with the same value as
18-- | A 'Dynamic' whose 'updated' 'Event' will never fire with the same value as
19-- the 'current' 'Behavior''s contents. In order to maintain this constraint,
19-- the 'current' 'Behavior''s contents. In order to maintain this constraint,
20−-- the value inside a 'NubDynamic' is always evaluated to
20+-- the value inside a 'UniqDynamic' is always evaluated to
21-- <https://wiki.haskell.org/Weak_head_normal_form weak head normal form>.
21-- <https://wiki.haskell.org/Weak_head_normal_form weak head normal form>.
23−-- Internally, 'NubDynamic' uses pointer equality as a heuristic to avoid
23+-- Internally, 'UniqDynamic' uses pointer equality as a heuristic to avoid
24-- unnecessary update propagation; this is much more efficient than performing
24-- unnecessary update propagation; this is much more efficient than performing
25−-- full comparisons. However, when the 'NubDynamic' is converted back into a
25+-- full comparisons. However, when the 'UniqDynamic' is converted back into a
26-- regular 'Dynamic', a full comparison is performed.
26-- regular 'Dynamic', a full comparison is performed.
27−newtype NubDynamic t a = NubDynamic { unNubDynamic :: Dynamic t a }
27+newtype UniqDynamic t a = UniqDynamic { unUniqDynamic :: Dynamic t a }
29−-- | Construct a 'NubDynamic' by eliminating redundant updates from a 'Dynamic'.
29+-- | Construct a 'UniqDynamic' by eliminating redundant updates from a 'Dynamic'.
30−nubDynamic :: Reflex t => Dynamic t a -> NubDynamic t a
30+uniqDynamic :: Reflex t => Dynamic t a -> UniqDynamic t a
31−nubDynamic d = NubDynamic $ unsafeBuildDynamic (sample $ current d) $ flip push (updated d) $ \new -> do --TODO: It would be very nice if we had an uncached push here
31+uniqDynamic d = UniqDynamic $ unsafeBuildDynamic (sample $ current d) $ flip push (updated d) $ \new -> do --TODO: It would be very nice if we had an uncached push here
32 old <- sample $ current d --TODO: Is it better to sample ourselves here?
32 old <- sample $ current d --TODO: Is it better to sample ourselves here?
33 return $ unsafeJustChanged old new
33 return $ unsafeJustChanged old new
35−-- | Retrieve a normal 'Dynamic' from a 'NubDynamic'. This will perform a final
35+-- | Retrieve a normal 'Dynamic' from a 'UniqDynamic'. This will perform a
36−-- check using the output type's 'Eq' instance to ensure deterministic behavior.
36+-- final check using the output type's 'Eq' instance to ensure deterministic
38-- WARNING: If used with a type whose 'Eq' instance is not law-abiding -
39-- WARNING: If used with a type whose 'Eq' instance is not law-abiding -
39−-- specifically, if there are cases where @x /= x@, 'fromNubDynamic' may
40+-- specifically, if there are cases where @x /= x@, 'fromUniqDynamic' may
40-- eliminate more 'updated' occurrences than it should. For example, NaN values
41-- eliminate more 'updated' occurrences than it should. For example, NaN values
41-- of 'Double' and 'Float' are considered unequal to themselves by the 'Eq'
42-- of 'Double' and 'Float' are considered unequal to themselves by the 'Eq'
42−-- instance, but can be equal by pointer equality. This may cause 'NubDynamic'
43+-- instance, but can be equal by pointer equality. This may cause 'UniqDynamic'
43-- to lose changes from NaN to NaN.
44-- to lose changes from NaN to NaN.
44−fromNubDynamic :: (Reflex t, Eq a) => NubDynamic t a -> Dynamic t a
45+fromUniqDynamic :: (Reflex t, Eq a) => UniqDynamic t a -> Dynamic t a
45−fromNubDynamic (NubDynamic d) = uniqDynBy superEq d
46+fromUniqDynamic (UniqDynamic d) = uniqDynBy superEq d
47 -- Only consider values different if they fail both pointer equality /and/
48 -- Only consider values different if they fail both pointer equality /and/
48 -- 'Eq' equality. This is to make things a bit more deterministic in the
49 -- 'Eq' equality. This is to make things a bit more deterministic in the
@@ -53,12 +54,12 @@ fromNubDynamic (NubDynamic d) = uniqDynBy superEq d
53 -- behavior deterministic in this case.
54 -- behavior deterministic in this case.
54 superEq a b = a `unsafePtrEq` b || a == b
55 superEq a b = a `unsafePtrEq` b || a == b
56−-- | Create a NubDynamic without nubbing it on creation. This will be slightly
57+-- | Create a UniqDynamic without uniqing it on creation. This will be slightly
57−-- faster than nubDynamic when used with a Dynamic whose values are always (or
58+-- faster than uniqDynamic when used with a Dynamic whose values are always (or
58-- nearly always) different from its previous values; if used with a Dynamic
59-- nearly always) different from its previous values; if used with a Dynamic
59−-- whose values do not change frequently, it may be much slower than nubDynamic
60+-- whose values do not change frequently, it may be much slower than uniqDynamic
60−alreadyNubbedDynamic :: Dynamic t a -> NubDynamic t a
61+alreadyUniqDynamic :: Dynamic t a -> UniqDynamic t a
61−alreadyNubbedDynamic = NubDynamic
62+alreadyUniqDynamic = UniqDynamic
63unsafePtrEq :: a -> a -> Bool
64unsafePtrEq :: a -> a -> Bool
64unsafePtrEq a b = case a `seq` b `seq` reallyUnsafePtrEquality# a b of
65unsafePtrEq a b = case a `seq` b `seq` reallyUnsafePtrEquality# a b of
@@ -71,30 +72,30 @@ unsafeJustChanged old new =
71 then Nothing
72 then Nothing
72 else Just new
73 else Just new
74−instance Reflex t => Accumulator t (NubDynamic t) where
75+instance Reflex t => Accumulator t (UniqDynamic t) where
75 accumMaybeM f z e = do
76 accumMaybeM f z e = do
76 let f' old change = do
77 let f' old change = do
77 mNew <- f old change
78 mNew <- f old change
78 return $ unsafeJustChanged old =<< mNew
79 return $ unsafeJustChanged old =<< mNew
79 d <- accumMaybeM f' z e
80 d <- accumMaybeM f' z e
80− return $ NubDynamic d
81+ return $ UniqDynamic d
81 mapAccumMaybeM f z e = do
82 mapAccumMaybeM f z e = do
82 let f' old change = do
83 let f' old change = do
83 (mNew, output) <- f old change
84 (mNew, output) <- f old change
84 return (unsafeJustChanged old =<< mNew, output)
85 return (unsafeJustChanged old =<< mNew, output)
85 (d, out) <- mapAccumMaybeM f' z e
86 (d, out) <- mapAccumMaybeM f' z e
86− return (NubDynamic d, out)
87+ return (UniqDynamic d, out)
88−instance Reflex t => Functor (NubDynamic t) where
89+instance Reflex t => Functor (UniqDynamic t) where
89− fmap f (NubDynamic d) = nubDynamic $ fmap f d
90+ fmap f (UniqDynamic d) = uniqDynamic $ fmap f d
91−instance Reflex t => Applicative (NubDynamic t) where
92+instance Reflex t => Applicative (UniqDynamic t) where
92− pure = NubDynamic . constDyn
93+ pure = UniqDynamic . constDyn
93− NubDynamic a <*> NubDynamic b = nubDynamic $ a <*> b
94+ UniqDynamic a <*> UniqDynamic b = uniqDynamic $ a <*> b
94 _ *> b = b
95 _ *> b = b
95 a <* _ = a
96 a <* _ = a
97−instance Reflex t => Monad (NubDynamic t) where
98+instance Reflex t => Monad (UniqDynamic t) where
98− NubDynamic x >>= f = nubDynamic $ x >>= unNubDynamic . f
99+ UniqDynamic x >>= f = uniqDynamic $ x >>= unUniqDynamic . f
99 _ >> b = b
100 _ >> b = b
100 return = pure
101 return = pure