Skip to content

Commit 658813b

Browse files
committed
Merge pull request #25 from purescript/rec
Use MonadRec to avoid blowing the stack
2 parents eb950d8 + 089fc98 commit 658813b

File tree

6 files changed

+60
-86
lines changed

6 files changed

+60
-86
lines changed

README.md

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -212,44 +212,41 @@ injC :: forall f g a. (Inject f g) => FreeC f a -> FreeC g a
212212
```
213213

214214

215-
#### `iterM`
215+
#### `runFree`
216216

217217
``` purescript
218-
iterM :: forall f m a. (Functor f, Monad m) => (forall a. f (m a) -> m a) -> Free f a -> m a
218+
runFree :: forall f a. (Functor f) => (f (Free f a) -> Free f a) -> Free f a -> a
219219
```
220220

221-
#### `goM`
221+
`runFree` runs a computation of type `Free f a`, using a function which unwraps a single layer of
222+
the functor `f` at a time.
222223

223-
``` purescript
224-
goM :: forall f m a. (Functor f, Monad m) => (f (Free f a) -> m (Free f a)) -> Free f a -> m a
225-
```
226-
227-
#### `go`
224+
#### `runFreeM`
228225

229226
``` purescript
230-
go :: forall f a. (Functor f) => (f (Free f a) -> Free f a) -> Free f a -> a
227+
runFreeM :: forall f m a. (Functor f, MonadRec m) => (f (Free f a) -> m (Free f a)) -> Free f a -> m a
231228
```
232229

230+
`runFreeM` runs a compuation of type `Free f a` in any `Monad` which supports tail recursion.
231+
See the `MonadRec` type class for more details.
233232

234-
#### `goEff`
233+
#### `runFreeC`
235234

236235
``` purescript
237-
goEff :: forall e f a. (Functor f) => (f (Free f a) -> Eff e (Free f a)) -> Free f a -> Eff e a
236+
runFreeC :: forall f a. (forall a. f a -> a) -> FreeC f a -> a
238237
```
239238

239+
`runFreeC` is the equivalent of `runFree` for type constructors transformed with `Coyoneda`,
240+
hence we have no requirement that `f` be a `Functor`.
240241

241-
#### `goMC`
242-
243-
``` purescript
244-
goMC :: forall f m a. (Monad m) => Natural f m -> FreeC f a -> m a
245-
```
246-
247-
#### `goEffC`
242+
#### `runFreeCM`
248243

249244
``` purescript
250-
goEffC :: forall e f a. Natural f (Eff e) -> FreeC f a -> Eff e a
245+
runFreeCM :: forall f m a. (MonadRec m) => Natural f m -> FreeC f a -> m a
251246
```
252247

248+
`runFreeCM` is the equivalent of `runFreeM` for type constructors transformed with `Coyoneda`,
249+
hence we have no requirement that `f` be a `Functor`.
253250

254251

255252
## Module Control.Monad.Trampoline

bower.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"purescript-lazy": "~0.3.0",
3232
"purescript-foldable-traversable": "~0.3.0",
3333
"purescript-coproducts": "~0.3.0",
34-
"purescript-inject": "~0.2.0"
34+
"purescript-inject": "~0.2.0",
35+
"purescript-tailrec": "~0.2.0"
3536
}
3637
}

examples/Teletype.purs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ teletypeN (PutStrLn s a) = const a <$> trace s
2020
teletypeN (GetLine k) = return $ k "fake input"
2121

2222
run :: forall a. Teletype a -> Eff (trace :: Trace) a
23-
run = goEffC teletypeN
23+
run = runFreeCM teletypeN
2424

2525
echo = do
2626
a <- getLine

examples/TeletypeCoproduct.purs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module TeletypeCoproduct where
33
import Control.Apply ((*>))
44
import Control.Alt ((<|>))
55
import Control.Monad.Eff (Eff())
6-
import Control.Monad.Free (FreeC(), liftFC, injC, goEffC)
6+
import Control.Monad.Free (FreeC(), liftFC, injC, runFreeCM)
77
import Data.Coyoneda (Natural())
88
import Data.Inject (prj)
99
import Data.Functor.Coproduct (Coproduct())
@@ -62,6 +62,6 @@ tN fa = fromJust $ (teletype1N <$> prj fa) <|>
6262
(teletype3N <$> prj fa)
6363

6464
run :: forall a. T a -> Eff (trace :: Trace) a
65-
run = goEffC tN
65+
run = runFreeCM tN
6666

6767
main = run u

src/Control/Monad/Free.purs

Lines changed: 38 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@ module Control.Monad.Free
55
, liftF, liftFC
66
, pureF, pureFC
77
, mapF, injC
8-
, iterM
9-
, goM, goMC
10-
, go
11-
, goEff, goEffC
8+
, runFree
9+
, runFreeM
10+
, runFreeC
11+
, runFreeCM
1212
) where
1313

1414
import Control.Monad.Trans
1515
import Control.Monad.Eff
16+
import Control.Monad.Rec.Class
17+
18+
import Data.Identity
1619
import Data.Coyoneda
1720
import Data.Either
1821
import Data.Function
@@ -69,69 +72,42 @@ mapF t fa = either (\s -> Free <<< t $ mapF t <$> s) Pure (resume fa)
6972
injC :: forall f g a. (Inject f g) => FreeC f a -> FreeC g a
7073
injC = mapF (liftCoyonedaT inj)
7174

72-
-- Note: can blow the stack!
73-
iterM :: forall f m a. (Functor f, Monad m) => (forall a. f (m a) -> m a) -> Free f a -> m a
74-
iterM _ (Pure a) = return a
75-
iterM k (Free f) = k $ iterM k <$> f
76-
iterM k (Gosub f) = f (\req recv -> iterM k (req unit) >>= (iterM k <<< recv))
77-
78-
-- Note: can blow the stack!
79-
goM :: forall f m a. (Functor f, Monad m) => (f (Free f a) -> m (Free f a)) -> Free f a -> m a
80-
goM k f = case resume f of
81-
Left s -> k s >>= goM k
82-
Right a -> return a
83-
84-
resumeGosub :: forall f a. (Functor f) => Free f a -> Either (f (Free f a)) (Free f a)
85-
resumeGosub (Gosub f) = f (\a g ->
86-
case a unit of
87-
Pure a -> Right (g a)
88-
Free t -> Left ((\h -> h >>= g) <$> t)
89-
Gosub h -> Right (h (\b i -> b unit >>= (\x -> i x >>= g)))
90-
)
91-
92-
unsafeLeft :: forall a b. Either a b -> a
93-
unsafeLeft (Left x) = x
94-
95-
unsafeRight :: forall a b. Either a b -> b
96-
unsafeRight (Right x) = x
97-
9875
resume :: forall f a. (Functor f) => Free f a -> Either (f (Free f a)) a
9976
resume f = case f of
10077
Pure x -> Right x
10178
Free x -> Left x
10279
g -> case resumeGosub g of
10380
Left l -> Left l
10481
Right r -> resume r
82+
where
83+
resumeGosub :: Free f a -> Either (f (Free f a)) (Free f a)
84+
resumeGosub (Gosub f) = f (\a g ->
85+
case a unit of
86+
Pure a -> Right (g a)
87+
Free t -> Left ((\h -> h >>= g) <$> t)
88+
Gosub h -> Right (h (\b i -> b unit >>= (\x -> i x >>= g)))
89+
)
90+
91+
-- | `runFree` runs a computation of type `Free f a`, using a function which unwraps a single layer of
92+
-- | the functor `f` at a time.
93+
runFree :: forall f a. (Functor f) => (f (Free f a) -> Free f a) -> Free f a -> a
94+
runFree fn = runIdentity <<< runFreeM (Identity <<< fn)
95+
96+
-- | `runFreeM` runs a compuation of type `Free f a` in any `Monad` which supports tail recursion.
97+
-- | See the `MonadRec` type class for more details.
98+
runFreeM :: forall f m a. (Functor f, MonadRec m) => (f (Free f a) -> m (Free f a)) -> Free f a -> m a
99+
runFreeM fn = tailRecM \f ->
100+
case resume f of
101+
Left fs -> Left <$> fn fs
102+
Right a -> return (Right a)
103+
104+
-- | `runFreeC` is the equivalent of `runFree` for type constructors transformed with `Coyoneda`,
105+
-- | hence we have no requirement that `f` be a `Functor`.
106+
runFreeC :: forall f a. (forall a. f a -> a) -> FreeC f a -> a
107+
runFreeC nat = runIdentity <<< runFreeCM (Identity <<< nat)
108+
109+
-- | `runFreeCM` is the equivalent of `runFreeM` for type constructors transformed with `Coyoneda`,
110+
-- | hence we have no requirement that `f` be a `Functor`.
111+
runFreeCM :: forall f m a. (MonadRec m) => Natural f m -> FreeC f a -> m a
112+
runFreeCM nat = runFreeM (liftCoyonedaTF nat)
105113

106-
go :: forall f a. (Functor f) => (f (Free f a) -> Free f a) -> Free f a -> a
107-
go fn f = case resume f of
108-
Left l -> go fn (fn l)
109-
Right r -> r
110-
111-
foreign import goEffImpl """
112-
function goEffImpl(resume, isRight, fromLeft, fromRight, fn, value) {
113-
return function(){
114-
while (true) {
115-
var r = resume(value);
116-
if (isRight(r)) return fromRight(r);
117-
value = fn(fromLeft(r))();
118-
}
119-
};
120-
}""" :: forall e f a. Fn6
121-
(Free f a -> Either (f (Free f a)) a)
122-
(Either (f (Free f a)) a -> Boolean)
123-
(Either (f (Free f a)) a -> (f (Free f a)))
124-
(Either (f (Free f a)) a -> a)
125-
(f (Free f a) -> Eff e (Free f a))
126-
(Free f a)
127-
(Eff e a)
128-
129-
goEff :: forall e f a. (Functor f) => (f (Free f a) -> Eff e (Free f a)) -> Free f a -> Eff e a
130-
goEff fn f = runFn6 goEffImpl resume isRight unsafeLeft unsafeRight fn f
131-
132-
-- Note: can blow the stack!
133-
goMC :: forall f m a. (Monad m) => Natural f m -> FreeC f a -> m a
134-
goMC nat = goM (liftCoyonedaTF nat)
135-
136-
goEffC :: forall e f a. Natural f (Eff e) -> FreeC f a -> Eff e a
137-
goEffC nat = goEff (liftCoyonedaTF nat)

src/Control/Monad/Trampoline.purs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ delay :: forall a. (Unit -> a) -> Trampoline a
2828
delay = delay' <<< defer
2929

3030
runTrampoline :: forall a. Trampoline a -> a
31-
runTrampoline = go force
31+
runTrampoline = runFree force

0 commit comments

Comments
 (0)