Skip to content

Commit e7e40d0

Browse files
author
Ryan Trinkle
committed
Merge branch 'develop'
# Conflicts: # default.nix
2 parents 638da82 + a9add3d commit e7e40d0

File tree

11 files changed

+680
-102
lines changed

11 files changed

+680
-102
lines changed

.ghci

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
:set -isrc
22
:set -hide-package MonadCatchIO-mtl
33
:set -hide-package monads-fd
4-
:set -XOverloadedStrings
4+
:seti -XOverloadedStrings

.travis.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
sudo: false
2+
before_install:
3+
- travis_retry cabal-$CABALVER update
4+
- export PATH=/opt/ghc/$GHCVER/bin:/opt/happy/1.19.5/bin:/opt/alex/3.1.4/bin:$PATH
5+
install:
6+
- travis_retry cabal-$CABALVER install --only-dependencies -j --enable-tests
7+
script:
8+
- cabal-$CABALVER configure -v2 --enable-tests
9+
- cabal-$CABALVER build
10+
- cabal-$CABALVER test
11+
- cabal-$CABALVER sdist # tests that a source-distribution can be generated
12+
# Check that the resulting source distribution can be built & installed.
13+
# If there are no other `.tar.gz` files in `dist`, this can be even simpler:
14+
# `cabal install --force-reinstalls dist/*-*.tar.gz`
15+
- SRC_TGZ=$(cabal-$CABALVER info . | awk '{print $2;exit}').tar.gz &&
16+
(cd dist && cabal-$CABALVER install --force-reinstalls "$SRC_TGZ")
17+
18+
matrix:
19+
include:
20+
- env: CABALVER=1.18 GHCVER=7.8.4
21+
addons: {apt: {packages: [cabal-install-1.18, ghc-7.8.4, alex-3.1.4, happy-1.19.5], sources: [hvr-ghc]}}
22+
- env: CABALVER=1.22 GHCVER=7.10.1
23+
addons: {apt: {packages: [cabal-install-1.22, ghc-7.10.1, alex-3.1.4, happy-1.19.5],sources: [hvr-ghc]}}
24+
fast_finish: true

default.nix

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
{ mkDerivation, dependent-map, dependent-sum
2-
, mtl, ref-tf, semigroups, these
2+
, mtl, ref-tf, semigroups, these, MemoTrie, exception-transformers
33
}:
44
mkDerivation {
55
pname = "reflex";
6-
version = "0.2";
6+
version = "0.3";
77
src = builtins.filterSource (path: type: baseNameOf path != ".git") ./.;
88
buildDepends = [
9-
dependent-map dependent-sum mtl ref-tf semigroups these
9+
dependent-map dependent-sum mtl ref-tf semigroups these exception-transformers
10+
];
11+
testDepends = [
12+
MemoTrie
1013
];
1114
license = null;
1215
}

reflex.cabal

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Name: reflex
2-
Version: 0.2
2+
Version: 0.3
33
Synopsis: Higher-order Functional Reactive Programming
44
Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
55
License: BSD3
@@ -23,9 +23,12 @@ library
2323
mtl >= 2.1 && < 2.3,
2424
containers == 0.5.*,
2525
these == 0.4.*,
26-
primitive == 0.5.*,
26+
primitive >= 0.5 && < 0.7,
2727
template-haskell >= 2.9 && < 2.11,
28-
ref-tf == 0.4.*
28+
ref-tf == 0.4.*,
29+
exception-transformers == 0.4.*,
30+
transformers >= 0.2,
31+
transformers-compat >= 0.3
2932

3033
exposed-modules:
3134
Reflex,
@@ -38,9 +41,24 @@ library
3841
Data.Functor.Misc
3942

4043
other-extensions: TemplateHaskell
41-
ghc-prof-options: -fprof-auto
4244
ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
4345

46+
test-suite cross-impl
47+
type: exitcode-stdio-1.0
48+
main-is: Reflex/Test/CrossImpl.hs
49+
other-modules:
50+
Reflex.Pure
51+
ghc-options: -O2 -main-is Reflex.Test.CrossImpl.test
52+
hs-source-dirs: test
53+
build-depends:
54+
base,
55+
reflex,
56+
ref-tf,
57+
mtl,
58+
containers,
59+
dependent-map,
60+
MemoTrie == 0.6.*
61+
4462
benchmark spider-bench
4563
type: exitcode-stdio-1.0
4664
hs-source-dirs: bench

src/Data/Functor/Misc.hs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{-# LANGUAGE KindSignatures, GADTs, DeriveDataTypeable, RankNTypes, ScopedTypeVariables #-}
1+
{-# LANGUAGE KindSignatures, GADTs, DeriveDataTypeable, RankNTypes, ScopedTypeVariables, PolyKinds #-}
22
module Data.Functor.Misc where
33

44
import Data.GADT.Compare
@@ -8,8 +8,9 @@ import Data.Dependent.Map (DMap, DSum (..))
88
import qualified Data.Dependent.Map as DMap
99
import Data.Typeable hiding (Refl)
1010
import Data.These
11+
import Data.Maybe
1112

12-
data WrapArg :: (* -> *) -> (* -> *) -> * -> * where
13+
data WrapArg :: (k -> *) -> (k -> *) -> * -> * where
1314
WrapArg :: f a -> WrapArg g f (g a)
1415

1516
instance GEq f => GEq (WrapArg g f) where
@@ -61,6 +62,9 @@ rewrapDMap f = DMap.fromDistinctAscList . map (\(WrapArg k :=> v) -> WrapArg k :
6162
unwrapDMap :: (forall a. f a -> a) -> DMap (WrapArg f k) -> DMap k
6263
unwrapDMap f = DMap.fromDistinctAscList . map (\(WrapArg k :=> v) -> k :=> f v) . DMap.toAscList
6364

65+
unwrapDMapMaybe :: (forall a. f a -> Maybe a) -> DMap (WrapArg f k) -> DMap k
66+
unwrapDMapMaybe f = DMap.fromDistinctAscList . catMaybes . map (\(WrapArg k :=> v) -> fmap (k :=>) $ f v) . DMap.toAscList
67+
6468
mapToDMap :: Map k v -> DMap (Const2 k v)
6569
mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v) . Map.toAscList
6670

src/Reflex/Class.hs

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TypeOperators, DeriveDataTypeable, PackageImports, TemplateHaskell, LambdaCase #-}
22
module Reflex.Class where
33

4-
import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl)
5-
4+
import Control.Applicative
65
import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
76
import Control.Monad.State.Strict hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
87
import Control.Monad.Reader hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
8+
import Control.Monad.Trans.Writer (WriterT())
9+
import Control.Monad.Trans.Except (ExceptT())
10+
import Control.Monad.Trans.Cont (ContT())
11+
import Control.Monad.Trans.RWS (RWST())
912
import Data.List.NonEmpty (NonEmpty (..))
1013
import Data.These
1114
import Data.Align
@@ -17,10 +20,14 @@ import Data.Dependent.Map (DMap, DSum (..), GCompare (..), GOrdering (..))
1720
import qualified Data.Dependent.Map as DMap
1821
import Data.Functor.Misc
1922
import Data.Semigroup
23+
import Data.Traversable
24+
25+
-- Note: must come last to silence warnings due to AMP on GHC < 7.10
26+
import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl)
2027

2128
import Debug.Trace (trace)
2229

23-
class (MonadHold t (PushM t), MonadSample t (PullM t), Functor (Event t), Functor (Behavior t)) => Reflex t where
30+
class (MonadHold t (PushM t), MonadSample t (PullM t), MonadFix (PushM t), Functor (Event t), Functor (Behavior t)) => Reflex t where
2431
-- | A container for a value that can change over time. Behaviors can be sampled at will, but it is not possible to be notified when they change
2532
data Behavior t :: * -> *
2633
-- | A stream of occurrences. During any given frame, an Event is either occurring or not occurring; if it is occurring, it will contain a value of the given type (its "occurrence type")
@@ -39,14 +46,14 @@ class (MonadHold t (PushM t), MonadSample t (PullM t), Functor (Event t), Functo
3946
type PullM t :: * -> *
4047
-- | Merge a collection of events; the resulting Event will only occur if at least one input event is occuring, and will contain all of the input keys that are occurring simultaneously
4148
merge :: GCompare k => DMap (WrapArg (Event t) k) -> Event t (DMap k) --TODO: Generalize to get rid of DMap use --TODO: Provide a type-level guarantee that the result is not empty
42-
-- | Efficiently fan-out an event to many destinations. This function should be partially applied, and then the result applied repeatedly to create child events
49+
-- | Efficiently fan-out an event to many destinations. This function should be partially applied, and then the result applied repeatedly to create child events
4350
fan :: GCompare k => Event t (DMap k) -> EventSelector t k --TODO: Can we help enforce the partial application discipline here? The combinator is worthless without it
4451
-- | Create an Event that will occur whenever the currently-selected input Event occurs
4552
switch :: Behavior t (Event t a) -> Event t a
4653
-- | Create an Event that will occur whenever the input event is occurring and its occurrence value, another Event, is also occurring
4754
coincidence :: Event t (Event t a) -> Event t a
4855

49-
class Monad m => MonadSample t m | m -> t where
56+
class (Applicative m, Monad m) => MonadSample t m | m -> t where
5057
-- | Get the current value in the Behavior
5158
sample :: Behavior t a -> m a
5259

@@ -66,14 +73,44 @@ instance MonadSample t m => MonadSample t (ReaderT r m) where
6673
instance MonadHold t m => MonadHold t (ReaderT r m) where
6774
hold a0 = lift . hold a0
6875

76+
instance (MonadSample t m, Monoid r) => MonadSample t (WriterT r m) where
77+
sample = lift . sample
78+
79+
instance (MonadHold t m, Monoid r) => MonadHold t (WriterT r m) where
80+
hold a0 = lift . hold a0
81+
82+
instance MonadSample t m => MonadSample t (StateT s m) where
83+
sample = lift . sample
84+
85+
instance MonadHold t m => MonadHold t (StateT s m) where
86+
hold a0 = lift . hold a0
87+
88+
instance MonadSample t m => MonadSample t (ExceptT e m) where
89+
sample = lift . sample
90+
91+
instance MonadHold t m => MonadHold t (ExceptT e m) where
92+
hold a0 = lift . hold a0
93+
94+
instance (MonadSample t m, Monoid w) => MonadSample t (RWST r w s m) where
95+
sample = lift . sample
96+
97+
instance (MonadHold t m, Monoid w) => MonadHold t (RWST r w s m) where
98+
hold a0 = lift . hold a0
99+
100+
instance MonadSample t m => MonadSample t (ContT r m) where
101+
sample = lift . sample
102+
103+
instance MonadHold t m => MonadHold t (ContT r m) where
104+
hold a0 = lift . hold a0
105+
69106
--------------------------------------------------------------------------------
70107
-- Convenience functions
71108
--------------------------------------------------------------------------------
72109

73110
-- | Create an Event from another Event.
74111
-- The provided function can sample 'Behavior's and hold 'Event's.
75112
pushAlways :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
76-
pushAlways f e = push (liftM Just . f) e
113+
pushAlways f = push (liftM Just . f)
77114

78115
-- | Flipped version of 'fmap'.
79116
ffor :: Functor f => f a -> (a -> b) -> f b
@@ -82,6 +119,28 @@ ffor = flip fmap
82119
instance Reflex t => Functor (Behavior t) where
83120
fmap f = pull . liftM f . sample
84121

122+
instance Reflex t => Applicative (Behavior t) where
123+
pure = constant
124+
f <*> x = pull $ sample f `ap` sample x
125+
_ *> b = b
126+
a <* _ = a
127+
128+
instance Reflex t => Monad (Behavior t) where
129+
a >>= f = pull $ sample a >>= sample . f
130+
-- Note: it is tempting to write (_ >> b = b); however, this would result in (fail x >> return y) succeeding (returning y), which violates the law that (a >> b = a >>= \_ -> b), since the implementation of (>>=) above actually will fail. Since we can't examine Behaviors other than by using sample, I don't think it's possible to write (>>) to be more efficient than the (>>=) above.
131+
return = constant
132+
fail = error "Monad (Behavior t) does not support fail"
133+
134+
instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
135+
a <> b = pull $ liftM2 (<>) (sample a) (sample b)
136+
sconcat = pull . liftM sconcat . mapM sample
137+
times1p n = fmap $ times1p n
138+
139+
instance (Reflex t, Monoid a) => Monoid (Behavior t a) where
140+
mempty = constant mempty
141+
mappend a b = pull $ liftM2 mappend (sample a) (sample b)
142+
mconcat = pull . liftM mconcat . mapM sample
143+
85144
--TODO: See if there's a better class in the standard libraries already
86145
-- | A class for values that combines filtering and mapping using 'Maybe'.
87146
class FunctorMaybe f where
@@ -239,7 +298,7 @@ dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
239298
-- 'Event's occurs. If both occur at the same time they are combined
240299
-- using 'mappend'.
241300
appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
242-
appendEvents e1 e2 = fmap (mergeThese mappend) $ align e1 e2
301+
appendEvents e1 e2 = mergeThese mappend <$> align e1 e2
243302

244303
{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
245304
sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
@@ -299,3 +358,9 @@ instance Reflex t => Align (Event t) where
299358
-- occurs and the 'Behavior' is true at the time of occurence.
300359
gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
301360
gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
361+
362+
-- | Create a new behavior given a starting behavior and switch to a the
363+
-- behvior carried by the event when it fires.
364+
switcher :: (Reflex t, MonadHold t m)
365+
=> Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
366+
switcher b eb = pull . (sample <=< sample) <$> hold b eb

src/Reflex/Dynamic.hs

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -156,16 +156,22 @@ mapDynM f d = do
156156
-- time the 'Event' occurs using a folding function on the previous
157157
-- value and the value of the 'Event'.
158158
foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
159-
foldDyn f = foldDynM (\o v -> return $ f o v)
159+
foldDyn f = foldDynMaybe $ \o v -> Just $ f o v
160160

161161
-- | Create a 'Dynamic' using the initial value and change it each
162162
-- time the 'Event' occurs using a monadic folding function on the
163163
-- previous value and the value of the 'Event'.
164164
foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
165-
foldDynM f z e = do
165+
foldDynM f = foldDynMaybeM $ \o v -> liftM Just $ f o v
166+
167+
foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
168+
foldDynMaybe f = foldDynMaybeM $ \o v -> return $ f o v
169+
170+
foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
171+
foldDynMaybeM f z e = do
166172
rec let e' = flip push e $ \o -> do
167173
v <- sample b'
168-
liftM Just $ f o v
174+
f o v
169175
b' <- hold z e'
170176
return $ Dynamic b' e'
171177

@@ -421,12 +427,20 @@ class RebuildSortedHList l where
421427
rebuildSortedHList :: [DSum (HListPtr l)] -> HList l
422428

423429
instance RebuildSortedHList '[] where
424-
rebuildSortedFHList [] = FHNil
425-
rebuildSortedHList [] = HNil
430+
rebuildSortedFHList l = case l of
431+
[] -> FHNil
432+
_ : _ -> error "rebuildSortedFHList{'[]}: empty list expected"
433+
rebuildSortedHList l = case l of
434+
[] -> HNil
435+
_ : _ -> error "rebuildSortedHList{'[]}: empty list expected"
426436

427437
instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
428-
rebuildSortedFHList ((WrapArg HHeadPtr :=> h) : t) = FHCons h $ rebuildSortedFHList $ map (\(WrapArg (HTailPtr p) :=> v) -> WrapArg p :=> v) t
429-
rebuildSortedHList ((HHeadPtr :=> h) : t) = HCons h $ rebuildSortedHList $ map (\(HTailPtr p :=> v) -> p :=> v) t
438+
rebuildSortedFHList l = case l of
439+
((WrapArg HHeadPtr :=> h) : t) -> FHCons h $ rebuildSortedFHList $ map (\(WrapArg (HTailPtr p) :=> v) -> WrapArg p :=> v) t
440+
_ -> error "rebuildSortedFHList{h':t}: non-empty list with HHeadPtr expected"
441+
rebuildSortedHList l = case l of
442+
((HHeadPtr :=> h) : t) -> HCons h $ rebuildSortedHList $ map (\(HTailPtr p :=> v) -> p :=> v) t
443+
_ -> error "rebuildSortedHList{h':t}: non-empty list with HHeadPtr expected"
430444

431445
dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) -> HList l
432446
dmapToHList = rebuildSortedHList . DMap.toList
@@ -449,12 +463,16 @@ class AllAreFunctors (f :: a -> *) (l :: [a]) where
449463

450464
instance AllAreFunctors f '[] where
451465
type FunctorList f '[] = '[]
452-
toFHList HNil = FHNil
466+
toFHList l = case l of
467+
HNil -> FHNil
468+
_ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
453469
fromFHList FHNil = HNil
454470

455471
instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
456472
type FunctorList f (h ': t) = f h ': FunctorList f t
457-
toFHList (a `HCons` b) = a `FHCons` toFHList b
473+
toFHList l = case l of
474+
a `HCons` b -> a `FHCons` toFHList b
475+
_ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
458476
fromFHList (a `FHCons` b) = a `HCons` fromFHList b
459477

460478
collectDyn :: ( RebuildSortedHList (HListElems b)
@@ -475,14 +493,20 @@ class IsHList a where
475493
instance IsHList (a, b) where
476494
type HListElems (a, b) = [a, b]
477495
toHList (a, b) = hBuild a b
478-
fromHList (a `HCons` b `HCons` HNil) = (a, b)
496+
fromHList l = case l of
497+
a `HCons` b `HCons` HNil -> (a, b)
498+
_ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
479499

480500
instance IsHList (a, b, c, d) where
481501
type HListElems (a, b, c, d) = [a, b, c, d]
482502
toHList (a, b, c, d) = hBuild a b c d
483-
fromHList (a `HCons` b `HCons` c `HCons` d `HCons` HNil) = (a, b, c, d)
503+
fromHList l = case l of
504+
a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
505+
_ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
484506

485507
instance IsHList (a, b, c, d, e, f) where
486508
type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
487509
toHList (a, b, c, d, e, f) = hBuild a b c d e f
488-
fromHList (a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil) = (a, b, c, d, e, f)
510+
fromHList l = case l of
511+
a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
512+
_ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139

0 commit comments

Comments
 (0)