diff --git a/docs/modules/IO.ts.md b/docs/modules/IO.ts.md
index c34918c69..178239787 100644
--- a/docs/modules/IO.ts.md
+++ b/docs/modules/IO.ts.md
@@ -184,7 +184,7 @@ Added in v2.0.0
**Signature**
```ts
-export const of = (a: A): IO => () => ...
+export const of = (a: A): IO => ...
```
Added in v2.0.0
diff --git a/src/IO.ts b/src/IO.ts
index f29525a80..69a14f5df 100644
--- a/src/IO.ts
+++ b/src/IO.ts
@@ -141,17 +141,17 @@ export function getMonoid(M: Monoid): Monoid> {
/**
* @since 2.0.0
*/
-export const of = (a: A): IO => () => a
+export const of = (a: A): IO => pureE(a)
/**
* @since 2.0.0
*/
export const io: Monad1 & MonadIO1 = {
URI,
- map: (ma, f) => () => f(ma()),
+ map: mapE,
of,
- ap: (mab, ma) => () => mab()(ma()),
- chain: (ma, f) => () => f(ma())(),
+ ap: applyE,
+ chain: bindE,
fromIO: identity
}
@@ -187,3 +187,73 @@ export {
*/
map
}
+
+const PURE = 'PURE'
+const MAP = 'MAP'
+const APPLY = 'APPLY'
+const BIND = 'BIND'
+const APPLY_FUNC = 'APPLY_FUNC'
+
+function pureE(x: any) {
+ return mkEff(PURE, x)
+}
+
+function mapE(effect: any, f: any) {
+ return mkEff(MAP, f, effect)
+}
+
+function applyE(effF: any, effect: any) {
+ return mkEff(APPLY, effect, effF)
+}
+
+function bindE(effect: any, f: any) {
+ return mkEff(BIND, f, effect)
+}
+
+function mkEff(tag: 'PURE' | 'MAP' | 'APPLY' | 'BIND' | 'APPLY_FUNC', _0: any, _1?: any) {
+ const effect = function $effect() {
+ return runEff($effect)
+ } as any
+ effect.tag = tag
+ effect._0 = _0
+ effect._1 = _1
+ return effect
+}
+
+function runEff(inputEff: any) {
+ let operations = []
+ let effect = inputEff
+ let res
+ let op
+ effLoop: for (;;) {
+ if (effect.tag !== undefined) {
+ if (effect.tag === MAP || effect.tag === BIND || effect.tag === APPLY) {
+ operations.push(effect)
+ effect = effect._1
+ continue
+ }
+ // here `tag === PURE`
+ res = effect._0
+ } else {
+ res = effect()
+ }
+
+ // tslint:disable-next-line no-conditional-assignment
+ while ((op = operations.pop())) {
+ if (op.tag === MAP) {
+ res = op._0(res)
+ } else if (op.tag === APPLY_FUNC) {
+ res = op._0(res)
+ } else if (op.tag === APPLY) {
+ effect = op._0
+ operations.push({ tag: APPLY_FUNC, _0: res })
+ continue effLoop
+ } else {
+ // op.tag === BIND
+ effect = op._0(res)
+ continue effLoop
+ }
+ }
+ return res
+ }
+}
diff --git a/test/IO.ts b/test/IO.ts
index e0dfb260e..5d171e90f 100644
--- a/test/IO.ts
+++ b/test/IO.ts
@@ -2,6 +2,7 @@ import * as assert from 'assert'
import { IO, getSemigroup, io, getMonoid } from '../src/IO'
import { semigroupSum } from '../src/Semigroup'
import { monoidSum } from '../src/Monoid'
+import { array, range } from '../src/Array'
describe('IO', () => {
it('ap', () => {
@@ -32,4 +33,8 @@ describe('IO', () => {
assert.strictEqual(M.concat(M.empty, append('b'))(), 2)
assert.deepStrictEqual(log, ['a', 'b'])
})
+
+ it('stack safe', () => {
+ array.sequence(io)(range(0, 20000).map(io.of))()
+ })
})
diff --git a/test/IOEither.ts b/test/IOEither.ts
index 5dab9e3e2..2b14bfb78 100644
--- a/test/IOEither.ts
+++ b/test/IOEither.ts
@@ -6,7 +6,7 @@ import { monoidString } from '../src/Monoid'
import { semigroupSum, semigroupString } from '../src/Semigroup'
import { none, some } from '../src/Option'
import { pipe, pipeable } from '../src/pipeable'
-import { getMonoid } from '../src/Array'
+import { getMonoid, array, range } from '../src/Array'
describe('IOEither', () => {
it('fold', () => {
@@ -283,4 +283,8 @@ describe('IOEither', () => {
assert.deepStrictEqual(r3(), _.left(['a'])())
})
})
+
+ it('stack safe', () => {
+ array.sequence(_.ioEither)(range(0, 20000).map(_.ioEither.of))()
+ })
})