on eb9d786 help sign in
11 files changed+512−13
since splitsnapshots
unifiedsplit
.gitignore+1 default.nix+6−3 reflex.cabal+18 src/Generics/SOP/DMapUtilities.hs+184 src/Generics/SOP/Distribute.hs+119 src/Reflex/Dynamic.hs+44−4 src/Reflex/Dynamic/CollectDynGeneric.hs+64 src/Reflex/Dynamic/TH.hs+5−4 test/MkDyn.hs+38 test/Reflex/Test/TH.hs+26 test/semantics.hs+7−2
.gitignore+1
@@ -36,3 +36,4 @@ hsenv.log
3636/ghci-tmp
3737*.dump-*
3838*.verbose-core2core
39+.stack-work/*
default.nix+6−3
@@ -2,13 +2,15 @@
22, dependent-map, dependent-sum, exception-transformers
33, haskell-src-exts, haskell-src-meta, hlint, lens, MemoTrie
44, monad-control, mtl, primitive, random, ref-tf
5−, semigroupoids , semigroups, split, stdenv, stm, syb
6−, template-haskell , these, time, transformers
5+, semigroupoids, semigroups, split, stdenv, stm, syb
6+, template-haskell, these, time, transformers
77, transformers-compat, unbounded-delays, prim-uniq
8+
89, data-default, filepath, directory, filemanip, ghcjs-base
9−, monoidal-containers, witherable
10+, monoidal-containers, witherable, generics-sop
1011, useTemplateHaskell ? true
1112}:
13+
1214mkDerivation {
1315 pname = "reflex";
1416 version = "0.5.0.1";
@@ -22,6 +24,7 @@ mkDerivation {
2224 base bifunctors containers deepseq dependent-map dependent-sum
2325 mtl ref-tf split transformers data-default
2426 random time unbounded-delays monoidal-containers witherable
27+ generics-sop
2528 ] ++ (if ghc.isGhcjs or false then [
2629 ghcjs-base
2730 ] else []) ++ (if !useTemplateHaskell then [] else [
reflex.cabal+18
@@ -47,6 +47,7 @@ library
4747 data-default >= 0.5 && < 0.8,
4848 dependent-map >= 0.2.4 && < 0.3,
4949 exception-transformers == 0.4.*,
50+ generics-sop >= 0.2.4.0,
5051 lens >= 4.7 && < 5,
5152 monad-control >= 1.0.1 && < 1.1,
5253 monoidal-containers == 0.4.*,
@@ -74,6 +75,8 @@ library
7475 Data.Functor.Misc,
7576 Data.Map.Misc,
7677 Data.WeakBag,
78+ Generics.SOP.DMapUtilities,
79+ Generics.SOP.Distribute,
7780 Reflex,
7881 Reflex.Class,
7982 Reflex.Adjustable.Class,
@@ -82,6 +85,7 @@ library
8285 Reflex.Collection,
8386 Reflex.Dynamic,
8487 Reflex.Dynamic.Uniq,
88+ Reflex.Dynamic.CollectDynGeneric,
8589 Reflex.DynamicWriter,
8690 Reflex.DynamicWriter.Base,
8791 Reflex.DynamicWriter.Class,
@@ -154,6 +158,8 @@ test-suite semantics
154158 main-is: semantics.hs
155159 hs-source-dirs: test
156160 ghc-options: -O2 -Wall -rtsopts
161+ if flag(use-template-haskell)
162+ cpp-options: -DUSE_TEMPLATE_HASKELL
157163 build-depends:
158164 base,
159165 bifunctors,
@@ -308,6 +314,18 @@ test-suite rootCleanup
308314 Reflex.TestPlan
309315 Test.Run
310316
317+test-suite mkDyn
318+ type: exitcode-stdio-1.0
319+ main-is: MkDyn.hs
320+ hs-source-dirs: test
321+ if !flag(use-template-haskell)
322+ buildable: False
323+ build-depends: base
324+ , reflex
325+ , deepseq
326+ , mtl
327+ , containers
328+
311329benchmark spider-bench
312330 type: exitcode-stdio-1.0
313331 hs-source-dirs: bench test
src/Generics/SOP/DMapUtilities.hsnew file+184
@@ -0,0 +1,184 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExplicitNamespaces #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE KindSignatures #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE PolyKinds #-}
9+{-# LANGUAGE RankNTypes #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE TypeFamilies #-}
12+{-# LANGUAGE TypeOperators #-}
13+{-|
14+Module : Generics.SOP.DMapUtilities
15+Description : Utilities for converting between the NS/NP types of generics-sop and Dependent Maps.
16+-}
17+
18+module Generics.SOP.DMapUtilities
19+ (
20+ -- * Type Functions
21+ FunctorWrapTypeList
22+ , FunctorWrapTypeListOfLists
23+
24+ -- * Types
25+ , TypeListTag
26+
27+ -- * Conversions
28+ -- ** 'NP' \<-\> 'DM.DMap'
29+ , npToDMap
30+ , dMapToNP
31+ -- ** 'NS' \<-\> 'DSum'
32+ , nsToDSum
33+ , dSumToNS
34+
35+ -- * Functor wrapping/unwrapping utilities for 'NP'
36+ , npUnCompose
37+ , nsOfnpUnCompose
38+ , npReCompose
39+ , nsOfnpReCompose
40+
41+ -- * Utilities
42+ , npSequenceViaDMap
43+
44+ -- * Proofs
45+ , functorWrappedSListIsSList
46+ )where
47+
48+import Generics.SOP ((:.:) (Comp), K (..), NP (..), NS (..), Proxy (..), SList (..), SListI (..), SListI2, fn,
49+ hcollapse, hmap, unComp)
50+import Generics.SOP.Dict (Dict (Dict), withDict)
51+import Generics.SOP.NP (sequence'_NP)
52+import Generics.SOP.NS (ap_NS)
53+
54+import qualified Data.Dependent.Map as DM
55+import Data.Dependent.Sum (DSum ((:=>)))
56+import qualified Data.Dependent.Sum as DS
57+
58+import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
59+
60+import Data.Functor.Identity (Identity (runIdentity))
61+
62+-- |A Tag type for making a 'DM.DMap' keyed by a type-level list
63+data TypeListTag (xs :: [k]) (x :: k) where -- x is in xs
64+ TLHead :: TypeListTag (x ': xs) x -- x begins xs
65+ TLTail :: TypeListTag xs x -> TypeListTag (y ': xs) x -- given that x is in xs, x is also in (y ': xs)
66+
67+instance GEq (TypeListTag xs) where
68+ geq TLHead TLHead = Just Refl
69+ geq (TLTail x) (TLTail y) = geq x y
70+ geq _ _ = Nothing
71+
72+instance GCompare (TypeListTag xs) where
73+ gcompare TLHead TLHead = GEQ
74+ gcompare TLHead (TLTail _) = GLT
75+ gcompare (TLTail _) TLHead = GGT
76+ gcompare (TLTail x) (TLTail y) = gcompare x y
77+
78+-- |Convert an 'NP' indexed by typelist xs into a 'DM.DMap' indexed by 'TypeListTag' xs
79+npToDMap :: NP f xs -> DM.DMap (TypeListTag xs) f
80+npToDMap Nil = DM.empty
81+npToDMap (fx :* npTail) = DM.insert TLHead fx $ DM.mapKeysMonotonic TLTail $ npToDMap npTail
82+
83+-- |Convert a 'DM.DMap' indexed by 'TypeListTag' xs to an 'NP'
84+-- |NB: This can fail since there is no guarantee that the 'DM.DMap' has entries for every tag. Hence it returns a 'Maybe'
85+dMapToNP :: forall xs f. SListI xs => DM.DMap (TypeListTag xs) f -> Maybe (NP f xs)
86+dMapToNP dm = sequence'_NP $ hmap (Comp . flip DM.lookup dm) makeTypeListTagNP
87+
88+-- |Convert a 'NS' indexed by a typelist xs to a 'DS.DSum' indexed by 'TypeListTag' xs
89+nsToDSum :: SListI xs => NS f xs -> DS.DSum (TypeListTag xs) f
90+nsToDSum = hcollapse . ap_NS (hmap (\tag -> (fn $ \val -> K (tag :=> val))) makeTypeListTagNP)
91+{- let nsFToNSDSum::SListI xs=>NS f xs -> NS (K (DS.DSum (TypeListTag xs) f)) xs
92+ nsFToNSDSum ns' = ap_NS (tagsToFs makeTypeListTagNP) ns'
93+ tagsToFs::SListI xs=>NP (TypeListTag xs) xs -> NP (f -.-> K (DS.DSum (TypeListTag xs) f)) xs
94+ tagsToFs = hmap (\tag -> (Fn $ \val -> K (tag :=> val)))
95+ in hcollapse . nsFToNSDSum
96+-}
97+
98+-- |Convert a 'DS.DSum' indexed by 'TypeListTag' xs into a 'NS' indexed by xs
99+dSumToNS :: DS.DSum (TypeListTag xs) f -> NS f xs
100+dSumToNS (tag :=> fa) = go tag fa
101+ where
102+ go::TypeListTag ys y -> f y -> NS f ys
103+ go TLHead fy = Z fy
104+ go (TLTail nextTag) fy = S $ go nextTag fy
105+
106+-- | Produce an NP of TypeListTags matching the type-list of the NP
107+makeTypeListTagNP :: SListI xs => NP (TypeListTag xs) xs
108+makeTypeListTagNP = go sList
109+ where
110+ go :: SList ys -> NP (TypeListTag ys) ys
111+ go SNil = Nil
112+ go SCons = TLHead :* hmap TLTail (go sList)
113+
114+-- these are here to allow moving functors in and out of typelists
115+type family FunctorWrapTypeList (f :: * -> *) (xs :: [*]) :: [*] where
116+ FunctorWrapTypeList f '[] = '[]
117+ FunctorWrapTypeList f (x ': xs) = f x ': FunctorWrapTypeList f xs
118+
119+type family FunctorWrapTypeListOfLists (f :: * -> *) (xss :: [[*]]) :: [[*]] where
120+ FunctorWrapTypeListOfLists f '[] = '[]
121+ FunctorWrapTypeListOfLists f (xs ': xsTail) = FunctorWrapTypeList f xs ': FunctorWrapTypeListOfLists f xsTail
122+
123+-- | Transform a type-list indexed product of composed functorial values into a type-list indexed product of functorial values where the inner part of the functor
124+-- composition has been moved to the type-list. The values in the product remain the same (up to types representing composition of the functors). E.g.,
125+--
126+-- > (f :.: g) 2 :* (f :.: g) 3.0 :* 'Nil :: NP (f :.: g) '[Int,Double] -> f (g 2) :* f (g 3.0) :* 'Nil :: NP f '[g Int, g Double]
127+npUnCompose :: forall f g xs. NP (f :.: g) xs -> NP f (FunctorWrapTypeList g xs)
128+npUnCompose = go
129+ where
130+ go :: NP (f :.: g) ys -> NP f (FunctorWrapTypeList g ys)
131+ go Nil = Nil
132+ go (fgx :* npTail) = unComp fgx :* go npTail
133+
134+-- | UnCompose all of the 'NP's in an "NS (NP f) xss".
135+nsOfnpUnCompose :: forall f g xss. (SListI2 xss) => NS (NP (f :.: g)) xss -> NS (NP f) (FunctorWrapTypeListOfLists g xss)
136+nsOfnpUnCompose = go sList where
137+ go :: forall yss. (SListI2 yss) => SList yss -> NS (NP (f :.: g)) yss -> NS (NP f) (FunctorWrapTypeListOfLists g yss)
138+ go SNil _ = error "An NS cannot be empty"
139+ go SCons (Z np) = Z (npUnCompose np)
140+ go SCons (S ns') = S (go sList ns')
141+
142+-- | The inverse of 'npUnCompose'. Given a type-list indexed product where all the types in the list are applications of the same functor,
143+-- remove that functor from all the types in the list and put it in the functor parameter of the 'NP'. The values in the product itself remain the same up
144+-- to types representing composition of the functors.
145+npReCompose :: forall f g xs. SListI xs => NP f (FunctorWrapTypeList g xs) -> NP (f :.: g) xs
146+npReCompose = go sList
147+ where
148+ go :: forall ys. SList ys -> NP f (FunctorWrapTypeList g ys) -> NP (f :.: g) ys
149+ go SNil Nil = Nil
150+ go SCons (fgx :* npTail) = Comp fgx :* go sList npTail
151+
152+-- | ReCompose all the 'NP's in an "NS (NP f) xss".
153+nsOfnpReCompose :: forall f g xss. (SListI2 xss) => NS (NP f) (FunctorWrapTypeListOfLists g xss) -> NS (NP (f :.: g)) xss
154+nsOfnpReCompose = go sList
155+ where
156+ go :: forall yss. (SListI2 yss) => SList yss -> NS (NP f) (FunctorWrapTypeListOfLists g yss) -> NS (NP (f :.: g)) yss
157+ go SNil _ = error "NS must be non-empty." -- this shouldn't happen since an NS can't be empty
158+ go SCons (Z np) = Z $ npReCompose np
159+ go SCons (S nsTail) = S $ go sList nsTail
160+
161+-- | Prove that "SListI xs=>(FunctorWrapTypeList f xs)" is also an instance of SListI
162+functorWrappedSListIsSList :: forall f xs. SListI xs => Proxy f -> SList xs -> Dict SListI (FunctorWrapTypeList f xs)
163+functorWrappedSListIsSList _ SNil = Dict
164+functorWrappedSListIsSList pf SCons = goCons (sList :: SList xs)
165+ where
166+ goCons :: forall y ys. SList (y ': ys) -> Dict SListI (FunctorWrapTypeList f (y ': ys))
167+ goCons SCons =
168+ let dict = functorWrappedSListIsSList pf $ (sList :: SList ys)
169+ in withDict dict Dict
170+
171+-- | sequence (in the sense of 'Data.Traversable.sequenceA') a functor f inside an 'NP'
172+-- using a function defined over a 'DM.DMap' indexed by the same type-level-list.
173+-- This is useful in cases where an efficient general solution exists for DMaps.
174+-- This can be done more simply for Applicative f but the efficiency will depend on
175+-- the particular functor and given function over 'DM.DMap'.
176+npSequenceViaDMap :: forall k (f :: * -> *) (g :: * -> *) (xs :: [*]). (Functor f, SListI xs
177+ , k ~ TypeListTag (FunctorWrapTypeList g xs))
178+ =>(DM.DMap k f -> f (DM.DMap k Identity))->NP (f :.: g) xs -> f (NP g xs)
179+npSequenceViaDMap sequenceDMap =
180+ withDict (functorWrappedSListIsSList (Proxy :: Proxy g) (sList :: SList xs)) $
181+ fmap (hmap (runIdentity . unComp) . npReCompose . (\(Just x) -> x) . dMapToNP) . sequenceDMap . npToDMap . npUnCompose
182+-- NB: The (\(Just x)->x) in there is safe!
183+-- dMapToNP has to return Maybe NP since the DMap could be incomplete.
184+-- But since we built this DMap from an NP, we know it's complete and dMapToNp will return a Just.
src/Generics/SOP/Distribute.hsnew file+119
@@ -0,0 +1,119 @@
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE KindSignatures #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE PolyKinds #-}
8+{-# LANGUAGE RankNTypes #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE TypeOperators #-}
11+module Generics.SOP.Distribute
12+ (
13+ expand
14+ , expandA
15+ , WrappedProjection
16+ , wrappedProjections
17+ , shiftWrappedProjection
18+ , WrappedInjection
19+ , wrappedInjections
20+ , shiftWrappedInjection
21+ , distributeNP
22+ , distributeI_NP
23+ , functorToNP
24+ , reAssociateNP
25+ , distributeToFields
26+ , reconstructA
27+ , functionPOPFromClass
28+ , Dict
29+ ) where
30+
31+import Generics.SOP hiding (Compose)
32+import Generics.SOP.Dict (Dict, withDict)
33+
34+-- | Turn a sum into a product with Maybes
35+expand :: forall (f :: [k] -> *) xs. (SListI xs) => NS f xs -> NP (Maybe :.: f) xs
36+expand ns = go sList (Just ns) where
37+ go :: forall ys. SList ys -> Maybe (NS f ys) -> NP (Maybe :.: f) ys
38+ go SNil _ = Nil
39+ go SCons mNS = case mNS of
40+ Nothing -> Comp Nothing :* go sList Nothing -- after Z
41+ Just ms -> case ms of
42+ Z fx -> Comp (Just fx) :* go sList Nothing -- at Z
43+ S ms' -> Comp Nothing :* go sList (Just ms') -- before Z
44+
45+-- | expand applied to a generics-sop generic type
46+expandA :: Generic a => a -> NP (Maybe :.: NP I) (Code a)
47+expandA = expand . unSOP . from
48+
49+-- | The type of projections from functor-wrapped values
50+type WrappedProjection (g :: * -> *) (f :: k -> *) (xs :: [k]) = K (g (NP f xs)) -.-> g :.: f
51+
52+-- | Create wrapped projections for a specific typelist xs
53+wrappedProjections :: forall xs g f. (Functor g,SListI xs) => NP (WrappedProjection g f xs) xs
54+wrappedProjections = case sList :: SList xs of
55+ SNil -> Nil
56+ SCons -> fn (Comp . fmap hd . unK) :* hliftA shiftWrappedProjection wrappedProjections
57+
58+-- | Utility for creating the product of WrappedProjections
59+shiftWrappedProjection :: Functor g => WrappedProjection g f xs a -> WrappedProjection g f (x ': xs) a
60+shiftWrappedProjection (Fn f) = Fn $ f . K . fmap tl . unK
61+
62+type WrappedInjection (g :: * -> *) (f :: k -> *) (xs :: [k]) = g :.: f -.-> K (g (NS f xs))
63+
64+wrappedInjections :: forall xs g f. (Functor g, SListI xs) => NP (WrappedInjection g f xs) xs
65+wrappedInjections = case sList :: SList xs of
66+ SNil -> Nil
67+ SCons -> fn (K . fmap Z . unComp) :* hliftA shiftWrappedInjection wrappedInjections
68+
69+shiftWrappedInjection :: Functor g => WrappedInjection g f xs a -> WrappedInjection g f (x ': xs) a
70+shiftWrappedInjection (Fn f) = Fn $ K . fmap S . unK . f
71+
72+-- | distribute a functor h from outside a product into each field in the product.
73+--
74+-- NB: For applicative h, this is an inverse of hsequence. If h is not applicative, then this is not invertible.
75+distributeNP :: (Functor h, SListI xs) => h (NP g xs) -> NP (h :.: g) xs
76+distributeNP x = hap wrappedProjections (hpure $ K x)
77+
78+-- | A special case of distribute when the product is parameterized over the identity
79+distributeI_NP :: (Functor h, SListI xs) => h (NP I xs) -> NP h xs
80+distributeI_NP = hmap (fmap unI . unComp) . distributeNP
81+
82+-- | Turn a functor wrapped value into a product of functor wrapped Maybes of products
83+-- with the outer product over constructors and the inner over fields
84+functorToNP :: forall g a.(Functor g,Generic a)=>g a -> NP (g :.: (Maybe :.: NP I)) (Code a)
85+functorToNP ga = hap wrappedProjections (hpure $ K (expandA <$> ga))
86+
87+-- | utility for manipulating functors in NPs
88+reAssociate :: Functor g => (g :.: (f :.: h)) a -> ((g :.: f) :.: h) a
89+reAssociate = Comp . Comp . fmap unComp . unComp
90+
91+-- | utility for manipulating functors in NPs
92+reAssociateNP :: (Functor g, SListI xss) => NP (g :.: (f :.: h)) xss->NP ((g :.: f) :.: h) xss
93+reAssociateNP = hmap reAssociate
94+
95+-- | Apply dsitributeI_NP to all constructors of an NS of an NP
96+distributeToFields :: (Functor g, SListI2 xss) => NP ((g :.: Maybe) :.: NP I) xss -> POP (g :.: Maybe) xss
97+distributeToFields =
98+ let proxyC = Proxy :: Proxy SListI
99+ in POP . hcliftA proxyC (distributeI_NP . unComp)
100+
101+-- | reconstruct the per constructor, functor wrapped value from an NP of (Maybe :.: NP)
102+reconstructA :: (Functor h, Generic a) => NP (h :.: NP I) (Code a) -> NP (K (h a)) (Code a)
103+reconstructA = hliftA (K . fmap (to . SOP) . unK) . hap wrappedInjections
104+
105+-- | Utility for turning a constraint into a POP (an NP of NP) of natural transformations at a given type.
106+functionPOPFromClass
107+ :: forall c f g xss
108+ . Dict (All2 c) xss
109+ -> (forall a. c a => f a -> g a)
110+ -> POP (f -.-> g) xss
111+functionPOPFromClass d f = withDict d $ hcpure (Proxy :: Proxy c) $ Fn f
112+
113+{-
114+functionPOPFromClass'::forall c f g xss.(All2 c xss, SListI2 xss)=>(forall a.c a=>f a -> g a)->POP (f -.-> g) xss
115+functionPOPFromClass' fn =
116+ let dict :: Dict (All2 c) xss
117+ dict = all_POP hdicts
118+ in withDict dict $ hcpure (Proxy :: Proxy c) $ Fn fn
119+-}
src/Reflex/Dynamic.hs+44−4
@@ -43,6 +43,7 @@ module Reflex.Dynamic
4343 , maybeDyn
4444 , eitherDyn
4545 , factorDyn
46+ , factorDynGeneric
4647 , scanDyn
4748 , scanDynMaybe
4849 , holdUniqDyn
@@ -76,7 +77,6 @@ module Reflex.Dynamic
7677 , unsafeDynamic
7778 ) where
7879
79−import Data.Functor.Compose
8080import Data.Functor.Misc
8181import Reflex.Class
8282
@@ -87,11 +87,15 @@ import Data.Align
8787import Data.Dependent.Map (DMap)
8888import qualified Data.Dependent.Map as DMap
8989import Data.Dependent.Sum (DSum (..))
90+import Data.Functor.Compose (Compose (Compose), getCompose)
9091import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
9192import Data.Map (Map)
92−import Data.Maybe
93+import Data.Maybe (isJust)
9394import Data.Monoid ((<>))
9495import Data.These
96+import Generics.SOP ((:.:) (Comp), Code, Generic, I (I), Proxy (Proxy), SListI, SOP (SOP), from, hcmap, hmap, to, unSOP)
97+import Generics.SOP.Distribute (distributeI_NP)
98+import Generics.SOP.DMapUtilities (FunctorWrapTypeListOfLists, dSumToNS, nsOfnpUnCompose, nsToDSum)
9599
96100import Debug.Trace
97101
@@ -308,6 +312,28 @@ eitherDyn = fmap (fmap unpack) . factorDyn . fmap eitherToDSum
308312 LeftTag :=> Compose a -> Left $ coerceDynamic a
309313 RightTag :=> Compose b -> Right $ coerceDynamic b
310314
315+-- | Use factorDyn and generics-sop to turn a Dynamic of a type into a factored
316+-- Dynamic of a type with Dynamic fields. For example:
317+--
318+-- > maybeDyn :: (Reflex t, MonadHold t m, MonadFix m) => Dynamic t (Maybe a) -> m (Dynamic t (Maybe (Dynamic t a)))
319+-- > maybeDyn = factorDynGeneric
320+--
321+-- > eitherDyn :: (Reflex t, MonadHold t m, MonadFix m) => Dynamic t (Either a b) -> m (Dynamic t (Either (Dynamic t a) (Dynamic t b)))
322+-- > eitherDyn = factorDynGeneric
323+factorDynGeneric :: forall t m a b. (Reflex t, MonadFix m, MonadHold t m
324+ , Generic a, Generic b
325+ , (Code b) ~ FunctorWrapTypeListOfLists (Dynamic t) (Code a))
326+ => Dynamic t a -> m (Dynamic t b)
327+factorDynGeneric da = do
328+ let dSumDyn = nsToDSum . unSOP . from <$> da -- Dynamic t (DSum (TypeListTag (Code a)) (NP I xs))
329+ dSumDyn' <- factorDyn dSumDyn -- Dynamic t (DSum (TypeListTag (Code a)) (NP I))
330+ let nsnpDyn = dSumToNS <$> dSumDyn' -- Dynamic t (NS (Compose (Dynamic t) (NP I)) (Code a))
331+ sListIC = Proxy :: Proxy SListI
332+ nsnpDyn' = hcmap sListIC (hmap (Comp. I) . distributeI_NP . getCompose) <$> nsnpDyn -- Dynamic t (NS (NP (I :.: Dynamic t)) (Code a))
333+ nsnpDyn'' = nsOfnpUnCompose <$> nsnpDyn' -- Dynamic t (NS (NP I) (Code b))
334+ result = to . SOP <$> nsnpDyn''
335+ return result
336+
311337factorDyn :: forall t m k v. (Reflex t, MonadHold t m, GEq k)
312338 => Dynamic t (DSum k v) -> m (Dynamic t (DSum k (Compose (Dynamic t) v)))
313339factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
@@ -359,15 +385,22 @@ demuxed d k =
359385 let e = select (demuxSelector d) (Const2 k)
360386 in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
361387
388+
362389--------------------------------------------------------------------------------
363390-- collectDyn
364391--------------------------------------------------------------------------------
365392
366−--TODO: This whole section is badly in need of cleanup
393+-- | This is now deprecated in favor of types from the generics-sop library and
394+-- functions provided in Reflex.Dynamic.CollectDynGeneric
395+-- The generics-sop 'NP' product type takes the place of FHList and HList.
396+-- Utilities to manipulate the NP types (shifting functors from the type into and out of the type-list
397+-- and convert from NP to DMap and DMap to NP are all in Generics.SOP.DMapUtilities
398+-- Equivalent "distribute" and "collect" functions are now in Reflex.Dynamic.CollectDynGeneric
367399
368400-- | A heterogeneous list whose type and length are fixed statically. This is
369401-- reproduced from the 'HList' package due to integration issues, and because
370402-- very little other functionality from that library is needed.
403+{-# DEPRECATED HList, HRevApp, hRevApp, hReverse, hBuild, HBuild' "This interface for type-level lists is being deprecated in favor of the 'NP' type from generics-sop" #-}
371404data HList (l::[*]) where
372405 HNil :: HList '[]
373406 HCons :: e -> HList l -> HList (e ': l)
@@ -400,6 +433,7 @@ instance HBuild' (a ': l) r
400433 hBuild' l x = hBuild' (HCons x l)
401434
402435-- | Like 'HList', but with a functor wrapping each element.
436+{-# DEPRECATED FHList,RebuildSortedHList "This interface for type-level lists is being deprecated in favor of the 'NP' type from generics-sop" #-}
403437data FHList f l where
404438 FHNil :: FHList f '[]
405439 FHCons :: f e -> FHList f l -> FHList f (e ': l)
@@ -417,6 +451,7 @@ instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dma
417451 HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
418452
419453-- | A typed index into a typed heterogeneous list.
454+{-# DEPRECATED HListPtr "Use 'Generics.SOP.DMapUtiltiies.TypeListTag' instead" #-}
420455data HListPtr l a where
421456 HHeadPtr :: HListPtr (h ': t) h
422457 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
@@ -424,6 +459,7 @@ data HListPtr l a where
424459deriving instance Eq (HListPtr l a)
425460deriving instance Ord (HListPtr l a)
426461
462+{-# DEPRECATED fhlistToDMap "Use generics-sop 'NP' for your functor wrapped type-list and then 'Generics.SOP.DMapUtilities.npToDMap'" #-}
427463fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
428464fhlistToDMap = DMap.fromList . go
429465 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
@@ -453,17 +489,20 @@ instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
453489 ((HHeadPtr :=> Identity h) : t) -> HCons h . rebuildSortedHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
454490 _ -> error "rebuildSortedHList{h':t}: non-empty list with HHeadPtr expected"
455491
492+{-# DEPRECATED dmapToHList "Use generics-sop 'NP' for your functor wrapped type-list and then 'Generics.SOP.DMapUtilities.dMapToNP'" #-}
456493dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
457494dmapToHList = rebuildSortedHList . DMap.toList
458495
459496-- | Collect a hetereogeneous list whose elements are all 'Dynamic's into a
460497-- single 'Dynamic' whose value represents the current values of all of the
461498-- input 'Dynamic's.
499+{-# DEPRECATED distributeFHListOverDynPure "Use the generics-sop 'NP' type for functor-wrapped type-lists and then 'Reflex.Dynamic.CollectDynGeneric.collectDynNP' " #-}
462500distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
463501distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
464502
465503-- | Indicates that all elements in a type-level list are applications of the
466504-- same functor.
505+{-# DEPRECATED AllAreFunctors "This functionality is provided for generics-sop 'NP' via 'Generics.SOP.DMapUtilties.npUnCompose' and 'Generics.SOP.DMapUtilities.npReCompose'" #-}
467506class AllAreFunctors (f :: a -> *) (l :: [a]) where
468507 type FunctorList f l :: [*]
469508 toFHList :: HList (FunctorList f l) -> FHList f l
@@ -489,7 +528,8 @@ instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
489528
490529-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
491530-- single 'Dynamic' whose value represents all the current values of the input's
492−-- constituent 'Dynamic's.
531+-- consitutent 'Dynamic's.
532+{-# DEPRECATED collectDynPure "Use 'Reflex.Dynamic.CollectDynGeneric.collectDynGeneric' instead." #-}
493533collectDynPure :: ( RebuildSortedHList (HListElems b)
494534 , IsHList a, IsHList b
495535 , AllAreFunctors (Dynamic t) (HListElems b)
src/Reflex/Dynamic/CollectDynGeneric.hsnew file+64
@@ -0,0 +1,64 @@
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE ExplicitNamespaces #-}
4+{-# LANGUAGE FlexibleContexts #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE GADTs #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE PolyKinds #-}
9+{-# LANGUAGE RankNTypes #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE TypeFamilies #-}
12+{-# LANGUAGE TypeOperators #-}
13+{-|
14+Module: Reflex.Dynamic.CollectDynGeneric
15+Description: Generic (generics-sop) implementation of CollectDynPure and distributeFHListOverDynPure
16+-}
17+module Reflex.Dynamic.CollectDynGeneric
18+ (
19+ collectDynGeneric
20+ , collectDynNP
21+ , distributeNPOverDyn
22+ ) where
23+
24+import Generics.SOP ((:.:) (Comp), Code, Generic, I (I), NP, NS, Proxy (..), SListI, SListI2, SOP (..), from,
25+ hcliftA, hliftA, hsequence', to, unComp, unI, unSOP)
26+
27+import Generics.SOP.DMapUtilities (FunctorWrapTypeList, FunctorWrapTypeListOfLists, npReCompose,
28+ npSequenceViaDMap, nsOfnpReCompose)
29+
30+import Reflex.Class (Reflex)
31+import Reflex.Dynamic (Dynamic, distributeDMapOverDynPure)
32+
33+-- | Take a type-list indexed product of dynamics and produce a dynamic of a type-list-indexed product of values (wrapped by an Identity functor, 'I').
34+collectDynNP :: (Reflex t, SListI xs) => NP (Dynamic t) xs -> Dynamic t (NP I xs)
35+collectDynNP = npSequenceViaDMap distributeDMapOverDynPure . hliftA (Comp . fmap I)
36+
37+-- | Given a pair of types a and b, each of which is an instance of 'Generics.SOP.Generic' (which is easily derived from GHC 'GHC.Generics.Generic')
38+-- and where a is "like" b except each field of each constructor is a Dynamic,
39+-- convert the former into a Dynamic of the latter. E.g.,
40+--
41+-- > (Dynamic t x, Dynamic t y) -> Dynamic t (x,y)
42+-- > Either (Dynamic t x) (Dynamic t y) -> Dynamic t (Either x y)
43+collectDynGeneric :: (Reflex t, Generic a, Generic b, (Code a) ~ FunctorWrapTypeListOfLists (Dynamic t) (Code b)) => a -> Dynamic t b
44+collectDynGeneric = fmap (to . SOP) . hsequence' . collectDynNSNP . aToNSNPI
45+
46+-- | A variation on 'collectDynNP' which more closely mirrors the structure of (deprecated) 'Reflex.Dynamic.distributeFHlistOverDynPure'
47+distributeNPOverDyn :: (Reflex t, SListI xs) => NP I (FunctorWrapTypeList (Dynamic t) xs) -> Dynamic t (NP I xs)
48+distributeNPOverDyn = collectDynNP . hliftA (unI . unComp) . npReCompose
49+
50+-- | Utility to help simplify collectDynGeneric: Take a generic type, a, which is Dynamic in every field and make an NS of an NP
51+aToNSNPI :: (Generic a, Code a ~ FunctorWrapTypeListOfLists (Dynamic t) xss, SListI2 xss) => a -> NS (NP (I :.: Dynamic t)) xss
52+aToNSNPI = nsOfnpReCompose . unSOP . from
53+
54+-- | Utility to help simplify collectDynGeneric: Take an NS of an NP of Dynamic fields and collect (or sequence)
55+-- the Dynamics from fields to constructors
56+collectDynNSNP :: (Reflex t, SListI2 xss) => NS (NP (I :.: Dynamic t)) xss -> NS (Dynamic t :.: NP I) xss
57+collectDynNSNP =
58+ let slistIC = Proxy :: Proxy SListI
59+ in hcliftA slistIC $ Comp . collectDynNP . hliftA (unI . unComp)
60+
61+
62+
63+
64+
src/Reflex/Dynamic/TH.hs+5−4
@@ -15,7 +15,8 @@ module Reflex.Dynamic.TH
1515 , mkDynPure
1616 ) where
1717
18−import Reflex.Dynamic
18+import Generics.SOP (I (..), NP (..))
19+import Reflex.Dynamic.CollectDynGeneric (collectDynNP)
1920
2021import Control.Monad.State
2122import Data.Data
@@ -44,9 +45,9 @@ qDynPure qe = do
4445 _ -> gmapM f d
4546 (e', exprsReversed) <- runStateT (gmapM f e) []
4647 let exprs = reverse exprsReversed
47− arg = foldr (\a b -> ConE 'FHCons `AppE` snd a `AppE` b) (ConE 'FHNil) exprs
48− param = foldr (\a b -> ConP 'HCons [VarP (fst a), b]) (ConP 'HNil []) exprs
49− [| $(return $ LamE [param] e') <$> distributeFHListOverDynPure $(return arg) |]
48+ arg = foldr (\a b -> ConE '(:*) `AppE` a `AppE` b) (ConE 'Nil) $ map snd exprs
49+ param = foldr (\a b -> ConP '(:*) [ConP 'I [VarP a], b]) (ConP 'Nil []) $ map fst exprs
50+ [| $(return $ LamE [param] e') <$> collectDynNP $(return arg) |]
5051
5152-- | Antiquote a 'Dynamic' expression. This can /only/ be used inside of a
5253-- 'qDyn' quotation.
test/MkDyn.hsnew file+38
@@ -0,0 +1,38 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE GADTs #-}
3+{-# LANGUAGE QuasiQuotes #-}
4+module Main where
5+
6+import Data.IntMap
7+import Reflex
8+import Reflex.Dynamic.TH
9+import Reflex.Plan.Pure
10+import Reflex.TestPlan
11+import System.Exit
12+
13+main::IO ()
14+main = do
15+ let setup = do
16+ pEv1 <- planList [1.0,2.0,3.0]
17+ pEv2 <- planList [-1.0,-2.0,-3.0]
18+ dyn1 <- holdDyn 0 pEv1
19+ dyn2 <- holdDyn 0 pEv2
20+ return (dyn1, dyn2)
21+ resMkDyn :: IntMap (Maybe Double)
22+ resMkDyn = testEvent $ runPure $ do
23+ (dyn1, dyn2) <- setup
24+ let dynFromMkDyn = [mkDynPure|$dyn1 + $dyn2|]
25+ return $ updated dynFromMkDyn
26+ resZipDyn :: IntMap (Maybe Double)
27+ resZipDyn = testEvent $ runPure $ do
28+ (dyn1,dyn2) <- setup
29+ let dynFromZipDyn = zipDynWith (+) dyn1 dyn2
30+ return $ updated dynFromZipDyn
31+ testPassed = resMkDyn == resZipDyn
32+ putStrLn $ "MkDynPure: " ++ show resMkDyn
33+ putStrLn $ "zipDynWith: " ++ show resZipDyn
34+ putStr "MkDyn: "
35+ if testPassed then putStrLn "Succeeded" else putStrLn "Failed"
36+ exitWith $ if testPassed then ExitSuccess else ExitFailure 1
37+
38+
test/Reflex/Test/TH.hsnew file+26
@@ -0,0 +1,26 @@
1+{-# LANGUAGE GADTs #-}
2+{-# LANGUAGE MultiParamTypeClasses #-}
3+{-# LANGUAGE QuasiQuotes #-}
4+module Reflex.Test.TH (testCases) where
5+
6+import Reflex
7+
8+import Reflex.TestPlan
9+
10+testCases :: [(String, TestCase)]
11+testCases =
12+ [
13+ testE "mkDynPure" $ do
14+ dyn1 <- holdDyn 0 =<< events1
15+ dyn2 <- holdDyn 0 =<< events2
16+ let dynResult = [mkDynPure|$dyn1 + $dyn2|]
17+ return $ updated dynResult
18+ ] where
19+
20+ events1, events2 :: TestPlan t m => m (Event t Int)
21+ events1 = plan [(1, 1), (11, 2), (21, 3), (31, 4), (41, 5)]
22+ events2 = plan [(5, 10), (15, 20), (25, 30), (35, 40), (45, 50)]
23+
24+
25+
26+
test/semantics.hs+7−2
@@ -1,10 +1,10 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE ConstraintKinds #-}
23{-# LANGUAGE FlexibleContexts #-}
34{-# LANGUAGE FlexibleInstances #-}
45{-# LANGUAGE GADTs #-}
56{-# LANGUAGE MultiParamTypeClasses #-}
67{-# LANGUAGE RankNTypes #-}
7−
88module Main (main) where
99
1010import Reflex.Test
@@ -14,7 +14,9 @@ import Data.Functor
1414import Data.List
1515import qualified Reflex.Bench.Focused as Focused
1616import qualified Reflex.Test.Micro as Micro
17−
17+#ifdef USE_TEMPLATE_HASKELL
18+import qualified Reflex.Test.TH as TH
19+#endif
1820import System.Environment
1921import System.Exit
2022
@@ -38,6 +40,9 @@ main = do
3840 where
3941 allTests = concat
4042 [ makeGroup "micro" Micro.testCases
43+#ifdef USE_TEMPLATE_HASKELL
44+ , makeGroup "TH" TH.testCases
45+#endif
4146 , makeGroup "subscribing (100,40)" (Focused.subscribing 100 40)
4247 , makeGroup "firing 1000" (Focused.firing 1000)
4348 , makeGroup "merge 100" (Focused.merging 100)