"use strict"
var __createBinding =
  (this && this.__createBinding) ||
  (Object.create
    ? function (o, m, k, k2) {
        if (k2 === undefined) k2 = k
        Object.defineProperty(o, k2, {
          enumerable: true,
          get: function () {
            return m[k]
          }
        })
      }
    : function (o, m, k, k2) {
        if (k2 === undefined) k2 = k
        o[k2] = m[k]
      })
var __setModuleDefault =
  (this && this.__setModuleDefault) ||
  (Object.create
    ? function (o, v) {
        Object.defineProperty(o, "default", { enumerable: true, value: v })
      }
    : function (o, v) {
        o["default"] = v
      })
var __importStar =
  (this && this.__importStar) ||
  function (mod) {
    if (mod && mod.__esModule) return mod
    var result = {}
    if (mod != null)
      for (var k in mod)
        if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
          __createBinding(result, mod, k)
    __setModuleDefault(result, mod)
    return result
  }
Object.defineProperty(exports, "__esModule", { value: true })
exports.gen =
  exports.GenIO =
  exports.succeedWith =
  exports.suspend =
  exports.zip_ =
  exports.zip =
  exports.zipWith_ =
  exports.zipWith =
  exports.unit =
  exports.map =
  exports.map_ =
  exports.succeed =
  exports.tap_ =
  exports.tap =
  exports.chain_ =
  exports.chain =
  exports.run =
  exports.IoURI =
    void 0
// tracing: off
/* eslint-disable prefer-const */
require("../Operator")
const Tp = __importStar(require("../Collections/Immutable/Tuple"))
const commons_1 = require("../Effect/commons")
const Stack_1 = require("../Stack")
const Utils_1 = require("../Utils")
exports.IoURI = Symbol()
class Base {}
class Succeed extends Base {
  constructor(a) {
    super()
    this.a = a
    this._iotag = "Succeed"
  }
}
class Suspend extends Base {
  constructor(f) {
    super()
    this.f = f
    this._iotag = "Suspend"
  }
}
class FlatMap extends Base {
  constructor(value, cont) {
    super()
    this.value = value
    this.cont = cont
    this._iotag = "FlatMap"
  }
}
/**
 * Runs this computation
 */
function run(self) {
  let stack = undefined
  let a = undefined
  let curIO = self
  while (curIO != null) {
    switch (curIO._iotag) {
      case "FlatMap": {
        switch (curIO.value._iotag) {
          case "Succeed": {
            curIO = curIO.cont(curIO.value.a)
            break
          }
          default: {
            stack = new Stack_1.Stack(curIO.cont, stack)
            curIO = curIO.value
          }
        }
        break
      }
      case "Suspend": {
        curIO = curIO.f()
        break
      }
      case "Succeed": {
        a = curIO.a
        if (stack) {
          curIO = stack.value(a)
          stack = stack.previous
        } else {
          curIO = undefined
        }
        break
      }
    }
  }
  return a
}
exports.run = run
/**
 * Extends this computation with another computation that depends on the
 * result of this computation by running the first computation, using its
 * result to generate a second computation, and running that computation.
 *
 * @dataFirst chain_
 */
function chain(f) {
  return (self) => new FlatMap(self, f)
}
exports.chain = chain
/**
 * Extends this computation with another computation that depends on the
 * result of this computation by running the first computation, using its
 * result to generate a second computation, and running that computation.
 */
function chain_(self, f) {
  return new FlatMap(self, f)
}
exports.chain_ = chain_
/**
 * Returns a computation that effectfully "peeks" at the success of this one.
 *
 * @dataFirst tap_
 */
function tap(f) {
  return (self) => tap_(self, f)
}
exports.tap = tap
/**
 * Returns a computation that effectfully "peeks" at the success of this one.
 */
function tap_(self, f) {
  return chain_(self, (a) => map_(f(a), () => a))
}
exports.tap_ = tap_
/**
 * Constructs a computation that always succeeds with the specified value.
 */
function succeed(a) {
  return new Succeed(a)
}
exports.succeed = succeed
/**
 * Extends this computation with another computation that depends on the
 * result of this computation by running the first computation, using its
 * result to generate a second computation, and running that computation.
 */
function map_(self, f) {
  return chain_(self, (a) => succeed(f(a)))
}
exports.map_ = map_
/**
 * Extends this computation with another computation that depends on the
 * result of this computation by running the first computation, using its
 * result to generate a second computation, and running that computation.
 *
 * @dataFirst map_
 */
function map(f) {
  return (self) => map_(self, f)
}
exports.map = map
/**
 * Constructs a computation that always returns the `Unit` value.
 */
exports.unit = new Succeed(undefined)
/**
 * Combines this computation with the specified computation combining the
 * results of both using the specified function.
 *
 * @dataFirst zipWith_
 */
function zipWith(that, f) {
  return (self) => zipWith_(self, that, f)
}
exports.zipWith = zipWith
/**
 * Combines this computation with the specified computation combining the
 * results of both using the specified function.
 */
function zipWith_(self, that, f) {
  return chain_(self, (a) => map_(that, (b) => f(a, b)))
}
exports.zipWith_ = zipWith_
/**
 * Combines this computation with the specified computation, combining the
 * results of both into a tuple.
 *
 * @dataFirst zip_
 */
function zip(that) {
  return (self) => zip_(self, that)
}
exports.zip = zip
/**
 * Combines this computation with the specified computation combining the
 * results of both into a tuple.
 */
function zip_(self, that) {
  return zipWith_(self, that, Tp.tuple)
}
exports.zip_ = zip_
/**
 * Suspend a computation, useful in recursion
 */
function suspend(f) {
  return new Suspend(f)
}
exports.suspend = suspend
/**
 * Lift a sync (non failable) computation
 */
function succeedWith(f) {
  return suspend(() => succeed(f()))
}
exports.succeedWith = succeedWith
class GenIO {
  constructor(effect) {
    this.effect = effect
  }
  *[Symbol.iterator]() {
    return yield this
  }
}
exports.GenIO = GenIO
function adapter(_) {
  return new GenIO(_)
}
function run_(state, iterator) {
  if (state.done) {
    return succeed(state.value)
  }
  return chain_(state.value["effect"], (val) => {
    const next = iterator.next(val)
    return run_(next, iterator)
  })
}
/**
 * Generator
 */
function gen(f) {
  return suspend(() => {
    const iterator = f(adapter)
    const state = iterator.next()
    return run_(state, iterator)
  })
}
exports.gen = gen
//# sourceMappingURL=index.js.map
