Skip to content

Commit e3f6277

Browse files
committed
Merge pull request #45 from natefaubion/forkall
Add forkAll combinator
2 parents e239f53 + a3e7e9b commit e3f6277

File tree

7 files changed

+138
-42
lines changed

7 files changed

+138
-42
lines changed

docs/Control.Monad.Aff.Class.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ class MonadAff e m where
99

1010
##### Instances
1111
``` purescript
12-
instance monadAffAff :: MonadAff e (Aff e)
13-
instance monadAffContT :: (Monad m, MonadAff eff m) => MonadAff eff (ContT r m)
14-
instance monadAffExceptT :: (Monad m, MonadAff eff m) => MonadAff eff (ExceptT e m)
15-
instance monadAffListT :: (Monad m, MonadAff eff m) => MonadAff eff (ListT m)
16-
instance monadAffMaybe :: (Monad m, MonadAff eff m) => MonadAff eff (MaybeT m)
17-
instance monadAffReader :: (Monad m, MonadAff eff m) => MonadAff eff (ReaderT r m)
18-
instance monadAffRWS :: (Monad m, Monoid w, MonadAff eff m) => MonadAff eff (RWST r w s m)
19-
instance monadAffState :: (Monad m, MonadAff eff m) => MonadAff eff (StateT s m)
20-
instance monadAffWriter :: (Monad m, Monoid w, MonadAff eff m) => MonadAff eff (WriterT w m)
12+
MonadAff e (Aff e)
13+
(Monad m, MonadAff eff m) => MonadAff eff (ContT r m)
14+
(Monad m, MonadAff eff m) => MonadAff eff (ExceptT e m)
15+
(Monad m, MonadAff eff m) => MonadAff eff (ListT m)
16+
(Monad m, MonadAff eff m) => MonadAff eff (MaybeT m)
17+
(Monad m, MonadAff eff m) => MonadAff eff (ReaderT r m)
18+
(Monad m, Monoid w, MonadAff eff m) => MonadAff eff (RWST r w s m)
19+
(Monad m, MonadAff eff m) => MonadAff eff (StateT s m)
20+
(Monad m, Monoid w, MonadAff eff m) => MonadAff eff (WriterT w m)
2121
```
2222

2323

docs/Control.Monad.Aff.Console.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#### `log`
44

55
``` purescript
6-
log :: forall e. String -> Aff (console :: CONSOLE | e) String
6+
log :: forall e. String -> Aff (console :: CONSOLE | e) Unit
77
```
88

99
Logs any string to the console. This basically saves you
@@ -12,10 +12,10 @@ from writing `liftEff $ log x` everywhere.
1212
#### `print`
1313

1414
``` purescript
15-
print :: forall e a. (Show a) => a -> Aff (console :: CONSOLE | e) a
15+
print :: forall e a. (Show a) => a -> Aff (console :: CONSOLE | e) Unit
1616
```
1717

18-
Prints any `Show`-able value to the console. This basically saves you
18+
Prints any `Show`-able value to the console. This basically saves you
1919
from writing `liftEff $ print x` everywhere.
2020

2121

docs/Control.Monad.Aff.Par.md

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
## Module Control.Monad.Aff.Par
22

3-
A newtype over `Aff` that provides `Applicative` instances that run in
4-
parallel. This is useful, for example, if you want to run a whole bunch
3+
A newtype over `Aff` that provides `Applicative` instances that run in
4+
parallel. This is useful, for example, if you want to run a whole bunch
55
of AJAX requests at the same time, rather than sequentially.
66

77
#### `Par`
@@ -13,14 +13,14 @@ newtype Par e a
1313

1414
##### Instances
1515
``` purescript
16-
instance semigroupPar :: (Semigroup a) => Semigroup (Par e a)
17-
instance monoidPar :: (Monoid a) => Monoid (Par e a)
18-
instance functorPar :: Functor (Par e)
19-
instance applyPar :: Apply (Par e)
20-
instance applicativePar :: Applicative (Par e)
21-
instance altPar :: Alt (Par e)
22-
instance plusPar :: Plus (Par e)
23-
instance alternativePar :: Alternative (Par e)
16+
(Semigroup a) => Semigroup (Par e a)
17+
(Monoid a) => Monoid (Par e a)
18+
Functor (Par e)
19+
Apply (Par e)
20+
Applicative (Par e)
21+
Alt (Par e)
22+
Plus (Par e)
23+
Alternative (Par e)
2424
```
2525

2626
#### `runPar`

docs/Control.Monad.Aff.md

+29-17
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,21 @@ This is moral equivalent of `ErrorT (ContT Unit (Eff e)) a`.
1313

1414
##### Instances
1515
``` purescript
16-
instance semigroupAff :: (Semigroup a) => Semigroup (Aff e a)
17-
instance monoidAff :: (Monoid a) => Monoid (Aff e a)
18-
instance functorAff :: Functor (Aff e)
19-
instance applyAff :: Apply (Aff e)
20-
instance applicativeAff :: Applicative (Aff e)
21-
instance bindAff :: Bind (Aff e)
22-
instance monadAff :: Monad (Aff e)
23-
instance monadEffAff :: MonadEff e (Aff e)
24-
instance monadErrorAff :: MonadError Error (Aff e)
25-
instance altAff :: Alt (Aff e)
26-
instance plusAff :: Plus (Aff e)
27-
instance alternativeAff :: Alternative (Aff e)
28-
instance monadPlusAff :: MonadPlus (Aff e)
29-
instance monadRecAff :: MonadRec (Aff e)
30-
instance monadContAff :: MonadCont (Aff e)
16+
(Semigroup a) => Semigroup (Aff e a)
17+
(Monoid a) => Monoid (Aff e a)
18+
Functor (Aff e)
19+
Apply (Aff e)
20+
Applicative (Aff e)
21+
Bind (Aff e)
22+
Monad (Aff e)
23+
MonadEff e (Aff e)
24+
MonadError Error (Aff e)
25+
Alt (Aff e)
26+
Plus (Aff e)
27+
Alternative (Aff e)
28+
MonadPlus (Aff e)
29+
MonadRec (Aff e)
30+
MonadCont (Aff e)
3131
```
3232

3333
#### `PureAff`
@@ -54,8 +54,8 @@ successfully canceled. The flag should not be used for communication.
5454

5555
##### Instances
5656
``` purescript
57-
instance semigroupCanceler :: Semigroup (Canceler e)
58-
instance monoidCanceler :: Monoid (Canceler e)
57+
Semigroup (Canceler e)
58+
Monoid (Canceler e)
5959
```
6060

6161
#### `cancel`
@@ -159,6 +159,18 @@ will not block on the result of the computation.
159159
Returns a canceler that can be used to attempt cancellation of the
160160
forked computation.
161161

162+
#### `forkAll`
163+
164+
``` purescript
165+
forkAll :: forall f e a. (Foldable f) => f (Aff e a) -> Aff e (Canceler e)
166+
```
167+
168+
Forks many asynchronous computation in a synchronous manner while being
169+
stack-safe up to the selected Foldable instance.
170+
171+
Returns a canceler that can be used to attempt cancellation of all
172+
forked computations.
173+
162174
#### `attempt`
163175

164176
``` purescript

src/Control/Monad/Aff.js

+55
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,61 @@ exports._forkAff = function (nonCanceler, aff) {
9696
};
9797
}
9898

99+
exports._forkAll = function (nonCanceler, foldl, affs) {
100+
var voidF = function(){};
101+
102+
return function(success, error) {
103+
var cancelers = foldl(function(acc) {
104+
return function(aff) {
105+
acc.push(aff(voidF, voidF));
106+
return acc;
107+
}
108+
})([])(affs);
109+
110+
var canceler = function(e) {
111+
return function(success, error) {
112+
var cancellations = 0;
113+
var result = false;
114+
var errored = false;
115+
116+
var s = function(bool) {
117+
cancellations = cancellations + 1;
118+
result = result || bool;
119+
120+
if (cancellations === cancelers.length && !errored) {
121+
try {
122+
success(result);
123+
} catch (e) {
124+
error(e);
125+
}
126+
}
127+
};
128+
129+
var f = function(err) {
130+
if (!errored) {
131+
errored = true;
132+
error(err);
133+
}
134+
};
135+
136+
for (var i = 0; i < cancelers.length; i++) {
137+
cancelers[i](e)(s, f);
138+
}
139+
140+
return nonCanceler;
141+
};
142+
};
143+
144+
try {
145+
success(canceler);
146+
} catch(e) {
147+
error(e);
148+
}
149+
150+
return nonCanceler;
151+
};
152+
}
153+
99154
exports._makeAff = function (cb) {
100155
return function(success, error) {
101156
return cb(function(e) {

src/Control/Monad/Aff.purs

+12
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module Control.Monad.Aff
88
, cancelWith
99
, finally
1010
, forkAff
11+
, forkAll
1112
, later
1213
, later'
1314
, launchAff
@@ -33,6 +34,7 @@ import Control.MonadPlus (MonadPlus)
3334
import Control.Plus (Plus)
3435

3536
import Data.Either (Either(..), either)
37+
import Data.Foldable (Foldable, foldl)
3638
import Data.Function (Fn2(), Fn3(), runFn2, runFn3)
3739
import Data.Monoid (Monoid, mempty)
3840

@@ -120,6 +122,14 @@ finally aff1 aff2 = do
120122
forkAff :: forall e a. Aff e a -> Aff e (Canceler e)
121123
forkAff aff = runFn2 _forkAff nonCanceler aff
122124

125+
-- | Forks many asynchronous computation in a synchronous manner while being
126+
-- | stack-safe up to the selected Foldable instance.
127+
-- |
128+
-- | Returns a canceler that can be used to attempt cancellation of all
129+
-- | forked computations.
130+
forkAll :: forall f e a. (Foldable f) => f (Aff e a) -> Aff e (Canceler e)
131+
forkAll affs = runFn3 _forkAll nonCanceler foldl affs
132+
123133
-- | Promotes any error to the value level of the asynchronous monad.
124134
attempt :: forall e a. Aff e a -> Aff e (Either Error a)
125135
attempt aff = runFn3 _attempt Left Right aff
@@ -207,6 +217,8 @@ foreign import _unsafeInterleaveAff :: forall e1 e2 a. Aff e1 a -> Aff e2 a
207217

208218
foreign import _forkAff :: forall e a. Fn2 (Canceler e) (Aff e a) (Aff e (Canceler e))
209219

220+
foreign import _forkAll :: forall f e a b. Fn3 (Canceler e) ((b -> a -> b) -> b -> f a -> b) (f (Aff e a)) (Aff e (Canceler e))
221+
210222
foreign import _makeAff :: forall e a. ((Error -> Eff e Unit) -> (a -> Eff e Unit) -> Eff e (Canceler e)) -> Aff e a
211223

212224
foreign import _pure :: forall e a. Fn2 (Canceler e) a (Aff e a)

test/Test/Main.purs

+20-3
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ module Test.Main where
22

33
import Prelude
44

5-
import Control.Monad.Aff (Aff(), runAff, later')
5+
import Control.Monad.Aff (Aff(), runAff, later', forkAll, cancel)
6+
import Control.Monad.Aff.AVar (AVAR(), makeVar', modifyVar, takeVar)
67
import Control.Monad.Cont.Class (callCC)
78
import Control.Monad.Eff (Eff())
89
import Control.Monad.Eff.Class (liftEff)
910
import Control.Monad.Eff.Console (CONSOLE(), log, print)
10-
import Control.Monad.Eff.Exception (EXCEPTION(), throwException)
11+
import Control.Monad.Eff.Exception (EXCEPTION(), throwException, error)
1112
import Control.Monad.Rec.Class (tailRecM)
1213

14+
import Data.Array (replicate)
1315
import Data.Either (Either(..))
1416

1517
loop :: forall eff. Int -> Aff (console :: CONSOLE | eff) Unit
@@ -20,13 +22,28 @@ loop n = tailRecM go n
2022
return (Right unit)
2123
go n = return (Left (n - 1))
2224

25+
all :: forall eff. Int -> Aff (console :: CONSOLE, avar :: AVAR | eff) Unit
26+
all n = do
27+
var <- makeVar' 0
28+
forkAll $ replicate n (modifyVar (+ 1) var)
29+
count <- takeVar var
30+
liftEff $ log ("Forked " <> show count)
31+
32+
cancelAll :: forall eff. Int -> Aff (console :: CONSOLE, avar :: AVAR | eff) Unit
33+
cancelAll n = do
34+
canceler <- forkAll $ replicate n (later' 100000 (liftEff $ log "oops"))
35+
canceled <- cancel canceler (error "bye")
36+
liftEff $ log ("Cancelled all: " <> show canceled)
37+
2338
delay :: forall eff. Int -> Aff eff Unit
2439
delay n = callCC \cont ->
2540
later' n (cont unit)
2641

27-
main :: Eff (console :: CONSOLE, err :: EXCEPTION) Unit
42+
main :: Eff (console :: CONSOLE, avar :: AVAR, err :: EXCEPTION) Unit
2843
main = runAff throwException (const (pure unit)) $ do
2944
liftEff $ log "pre-delay"
3045
delay 1000
3146
liftEff $ log "post-delay"
3247
loop 1000000
48+
all 100000
49+
cancelAll 100000

0 commit comments

Comments
 (0)