"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
  }
var _a
Object.defineProperty(exports, "__esModule", { value: true })
exports.provideSome =
  exports.provideSome_ =
  exports.unit =
  exports.retry =
  exports.ensuring =
  exports.ensuring_ =
  exports.foldM =
  exports.foldM_ =
  exports.catchAll =
  exports.catchAll_ =
  exports.chain =
  exports.chain_ =
  exports.map =
  exports.map_ =
  exports.dieWith =
  exports.die =
  exports.failWith =
  exports.fail =
  exports.succeedWith =
  exports.succeed =
  exports.isRetryException =
  exports.STMRetryException =
  exports.RetryExceptionTypeId =
  exports.isDieException =
  exports.STMDieException =
  exports.DieExceptionTypeId =
  exports.isFailException =
  exports.STMFailException =
  exports.FailExceptionTypeId =
  exports.concreteSTM =
  exports.STMProvideSome =
  exports.STMProvideSomeTypeId =
  exports.STMSucceedNow =
  exports.STMSucceedNowTypeId =
  exports.STMSucceed =
  exports.STMSucceedTypeId =
  exports.STMOnSuccess =
  exports.STMOnSuccessTypeId =
  exports.STMOnRetry =
  exports.STMOnRetryTypeId =
  exports.STMOnFailure =
  exports.STMOnFailureTypeId =
  exports.STMEffect =
  exports.STMEffectTypeId =
  exports.STM =
  exports.STMTypeId =
    void 0
const tracing_1 = __importStar(require("../../../Tracing"))
const fileName_1 = "(@effect-ts/system): _src/Transactional/STM/_internal/primitives.ts"
const T = __importStar(require("../../../Effect"))
const E = __importStar(require("../../../Either"))
exports.STMTypeId = Symbol()
/**
 * `STM<R, E, A>` represents an effect that can be performed transactionally,
 *  resulting in a failure `E` or a value `A` that may require an environment
 *  `R` to execute.
 *
 * Software Transactional Memory is a technique which allows composition of arbitrary atomic operations.  It is
 *  the software analog of transactions in database systems.
 *
 * The API is lifted directly from the Haskell package Control.Concurrent.STM although the implementation does not
 *  resemble the Haskell one at all.
 *  [[http://hackage.haskell.org/package/stm-2.5.0.0/docs/Control-Concurrent-STM.html]]
 *
 * STM in Haskell was introduced in:
 *  Composable memory transactions, by Tim Harris, Simon Marlow, Simon Peyton Jones, and Maurice Herlihy, in ACM
 *  Conference on Principles and Practice of Parallel Programming 2005.
 *  [[https://www.microsoft.com/en-us/research/publication/composable-memory-transactions/]]
 *
 * See also:
 *  Lock Free Data Structures using STMs in Haskell, by Anthony Discolo, Tim Harris, Simon Marlow, Simon Peyton Jones,
 *  Satnam Singh) FLOPS 2006: Eighth International Symposium on Functional and Logic Programming, Fuji Susono, JAPAN,
 *  April 2006
 *  [[https://www.microsoft.com/en-us/research/publication/lock-free-data-structures-using-stms-in-haskell/]]
 *
 * The implemtation is based on the ZIO STM module, while JS environments have no race conditions from multiple threads
 *  STM provides greater benefits for syncronisation of Fibers and transactional data-types can be quite useful.
 */
class STM {
  constructor() {
    this[_a] = exports.STMTypeId
  }
}
exports.STM = STM
;(_a = exports.STMTypeId), T._R, T._E, T._A
exports.STMEffectTypeId = Symbol()
class STMEffect extends STM {
  constructor(f) {
    super()
    this.f = f
    this._typeId = exports.STMEffectTypeId
  }
}
exports.STMEffect = STMEffect
exports.STMOnFailureTypeId = Symbol()
class STMOnFailure extends STM {
  constructor(stm, onFailure) {
    super()
    this.stm = stm
    this.onFailure = onFailure
    this._typeId = exports.STMOnFailureTypeId
  }
  apply(a) {
    return new STMSucceedNow(a)
  }
}
exports.STMOnFailure = STMOnFailure
exports.STMOnRetryTypeId = Symbol()
class STMOnRetry extends STM {
  constructor(stm, onRetry) {
    super()
    this.stm = stm
    this.onRetry = onRetry
    this._typeId = exports.STMOnRetryTypeId
  }
  apply(a) {
    return new STMSucceedNow(a)
  }
}
exports.STMOnRetry = STMOnRetry
exports.STMOnSuccessTypeId = Symbol()
class STMOnSuccess extends STM {
  constructor(stm, apply) {
    super()
    this.stm = stm
    this.apply = apply
    this._typeId = exports.STMOnSuccessTypeId
  }
}
exports.STMOnSuccess = STMOnSuccess
exports.STMSucceedTypeId = Symbol()
class STMSucceed extends STM {
  constructor(a) {
    super()
    this.a = a
    this._typeId = exports.STMSucceedTypeId
  }
}
exports.STMSucceed = STMSucceed
exports.STMSucceedNowTypeId = Symbol()
class STMSucceedNow extends STM {
  constructor(a) {
    super()
    this.a = a
    this._typeId = exports.STMSucceedNowTypeId
  }
}
exports.STMSucceedNow = STMSucceedNow
exports.STMProvideSomeTypeId = Symbol()
class STMProvideSome extends STM {
  constructor(stm, f) {
    super()
    this.stm = stm
    this.f = f
    this._typeId = exports.STMProvideSomeTypeId
  }
}
exports.STMProvideSome = STMProvideSome
/**
 * @optimize remove
 */
function concreteSTM(_) {
  //
}
exports.concreteSTM = concreteSTM
exports.FailExceptionTypeId = Symbol()
class STMFailException {
  constructor(e) {
    this.e = e
    this._typeId = exports.FailExceptionTypeId
  }
}
exports.STMFailException = STMFailException
function isFailException(u) {
  return (
    typeof u === "object" &&
    u != null &&
    "_typeId" in u &&
    u["_typeId"] === exports.FailExceptionTypeId
  )
}
exports.isFailException = isFailException
exports.DieExceptionTypeId = Symbol()
class STMDieException {
  constructor(e) {
    this.e = e
    this._typeId = exports.DieExceptionTypeId
  }
}
exports.STMDieException = STMDieException
function isDieException(u) {
  return (
    typeof u === "object" &&
    u != null &&
    "_typeId" in u &&
    u["_typeId"] === exports.DieExceptionTypeId
  )
}
exports.isDieException = isDieException
exports.RetryExceptionTypeId = Symbol()
class STMRetryException {
  constructor() {
    this._typeId = exports.RetryExceptionTypeId
  }
}
exports.STMRetryException = STMRetryException
function isRetryException(u) {
  return (
    typeof u === "object" &&
    u != null &&
    "_typeId" in u &&
    u["_typeId"] === exports.RetryExceptionTypeId
  )
}
exports.isRetryException = isRetryException
//
// primitive ops
//
/**
 * Returns an `STM` effect that succeeds with the specified value.
 */
function succeed(a) {
  return new STMSucceedNow(a)
}
exports.succeed = succeed
/**
 * Returns an `STM` effect that succeeds with the specified value.
 */
function succeedWith(a) {
  return new STMSucceed(a)
}
exports.succeedWith = succeedWith
/**
 * Returns a value that models failure in the transaction.
 */
function fail(e) {
  return new STMEffect(() => {
    throw new STMFailException(e)
  })
}
exports.fail = fail
/**
 * Returns a value that models failure in the transaction.
 */
function failWith(e) {
  return new STMEffect(() => {
    throw new STMFailException(e())
  })
}
exports.failWith = failWith
/**
 * Kills the fiber running the effect.
 */
function die(u) {
  return new STMEffect(() => {
    throw new STMDieException(u)
  })
}
exports.die = die
/**
 * Kills the fiber running the effect.
 */
function dieWith(u) {
  return new STMEffect(() => {
    throw new STMDieException(u())
  })
}
exports.dieWith = dieWith
/**
 * Maps the value produced by the effect.
 */
function map_(self, f) {
  return chain_(self, (a) => succeed(f(a)))
}
exports.map_ = map_
/**
 * Maps the value produced by the effect.
 *
 * @dataFirst map_
 */
function map(f) {
  return (self) => map_(self, f)
}
exports.map = map
/**
 * Feeds the value produced by this effect to the specified function,
 * and then runs the returned effect as well to produce its results.
 */
function chain_(self, f) {
  return new STMOnSuccess(self, f)
}
exports.chain_ = chain_
/**
 * Feeds the value produced by this effect to the specified function,
 * and then runs the returned effect as well to produce its results.
 *
 * @dataFirst chain_
 */
function chain(f) {
  return (self) => chain_(self, f)
}
exports.chain = chain
/**
 * Recovers from all errors.
 */
function catchAll_(self, f) {
  return new STMOnFailure(self, f)
}
exports.catchAll_ = catchAll_
/**
 * Recovers from all errors.
 *
 * @dataFirst catchAll_
 */
function catchAll(f) {
  return (self) => catchAll_(self, f)
}
exports.catchAll = catchAll
/**
 * Effectfully folds over the `STM` effect, handling both failure and
 * success.
 */
function foldM_(self, g, f) {
  return chain_(
    catchAll_(map_(self, E.right), (e) => map_(g(e), E.left)),
    E.fold(succeed, f)
  )
}
exports.foldM_ = foldM_
/**
 * Effectfully folds over the `STM` effect, handling both failure and
 * success.
 *
 * @dataFirst foldM_
 */
function foldM(g, f) {
  return (self) => foldM_(self, g, f)
}
exports.foldM = foldM
/**
 * Executes the specified finalization transaction whether or
 * not this effect succeeds. Note that as with all STM transactions,
 * if the full transaction fails, everything will be rolled back.
 */
function ensuring_(self, finalizer) {
  return foldM_(
    self,
    (e) => chain_(finalizer, () => fail(e)),
    (a) => chain_(finalizer, () => succeed(a))
  )
}
exports.ensuring_ = ensuring_
/**
 * Executes the specified finalization transaction whether or
 * not this effect succeeds. Note that as with all STM transactions,
 * if the full transaction fails, everything will be rolled back.
 *
 * @dataFirst ensuring_
 */
function ensuring(finalizer) {
  return (self) => ensuring_(self, finalizer)
}
exports.ensuring = ensuring
/**
 * Abort and retry the whole transaction when any of the underlying
 * transactional variables have changed.
 */
exports.retry = new STMEffect(() => {
  throw new STMRetryException()
})
/**
 * Returns an `STM` effect that succeeds with `Unit`.
 */
exports.unit = succeed(undefined)
/**
 * Provides some of the environment required to run this effect,
 * leaving the remainder `R0`.
 */
function provideSome_(self, f) {
  return new STMProvideSome(self, f)
}
exports.provideSome_ = provideSome_
/**
 * Provides some of the environment required to run this effect,
 * leaving the remainder `R0`.
 *
 * @dataFirst provideSome_
 */
function provideSome(f) {
  return (self) => provideSome_(self, f)
}
exports.provideSome = provideSome
//# sourceMappingURL=primitives.js.map
